Day9 Django 基礎

# views

def index(request)
    return render(request, 'blogs/index.html')

これはrenderメソッドでrequest情報を元にして、blogs/index.htmlを返すよという意味。

# models.py

def __str__(self):
    return self.title

とすると、モデルで設定したものを、adminで表示する時に使うことができる。

# views

def index(request):
    blogs = Blog.objects.order_by('-created_datetime')
    return render(request, 'blogs/index.html', {'blogs':blogs})


# views

def index(request):
    blogs = Blog.objects.order_by('-created_datetime')
    params = {
        'blogs':blogs
    }
    return render(request, 'blogs/index.html', params)
    


ここの、{'blogs':blogs}でテンプレートに渡すことをしている。これは辞書型でキーが'blogs'、値がblogsとなる。またparamsで定義すれば、paramsを渡すこともできる。

#html

<a href="{% url 'blogs:detail' blog_id=blog.id%}">記事を読む</a>

これはblogsアプリケーション内のdetailという名前(urls.py)を選択しているという意味。
blog_id=blog.idというのはblog_idという変数にblogのidを代入している。blog.idでidを取れるから、それを変数に代入している。これでid情報をurlに渡すことができる。



・GETメソッド:情報の読み取り、取得、(サーバーからデータを返してほしい時)
・POSTメソッド:情報の送信、投稿、(サーバーに対して何かしらのデータを送りたい時。データベースに新しい情報を追加することができる。)

# views

def new(request)
    if request.method == POST:
        form = BlogForm(request.POST)
        ・
        ・
        ・

・request.POST........ユーザーがフォームに入力した情報が含まれている。その情報を元に新しいBlogFormインスタンスを生成するという処理になる。
・form = BlogForm(request.POST)を引数にrequest.POSTを取るだけでこのような処理ができる。これはBlogFormがModelFormを継承しているから。

# views 

def edit(request, blog_id):
    blog = get_object_or_404(Blog, id=blog_id)
    form = BlogForm(instance=blog)
    return render(request, 'blogs/edit.html', {'form':form, 'blog': blog})

・form = BlogForm(instance=blog).........instance=Blogで編集内容の中身を保持しておく。

def edit(request, blog_id):
    blog = get_object_or_404(Blog, id=blog_id)
    if request.method == "POST":
        form = BlogForm(request.POST, instance=blog)
        if form.is_valid():
            form.save()
            return redirect('blogs:detail', blog_id=blog_id)
    else:
        form = BlogForm(instance=blog)
    return render(request, 'blogs/edit.html', {'form':form, 'blog': blog})

・form = BlogForm(request.POST, instance=blog)........編集の時には、既に特定の記事のインスタンスがあるため、どのインスタンスに対してフォームを作用させるかを指定する必要がある。それがinstance=blogを引数に取る理由。

今日は以上!

Day8 stackの続き

class InfixToPostfix(object):
    """
    InfixToPostfix つまり中置から後置
    get the postfix of the given infix expression
    """

    def __init__(self, expression=None, stack=None):
        """
        :param expression: the infix expression to be converted to postfix
        :param stack: stack to perform infix to postfix operation
        """
        self.expression = list(expression)
        self.my_stack = stack

    @staticmethod
    def _is_operand(char):
        """
        utility function to find whether the given character is an operator
        """
        # OLD VERSION
        # return ord(char) >= ord('a') and ord(char) <= ord('z') \
        #        or ord(char) >= ord('A') and ord(char) <= ord('Z')
        return True if ord(char) in [ord(c) for c in ascii_letters] else False

    @staticmethod
    def _precedence(char):
        """
        utility function to find precedence of the specified character
        """
        if char == '+' or char == '-':
            return 1
        elif char == '*' or char == '/':
            return 2
        elif char == '^':
            return 3
        else:
            return -1

    def infix_to_postfix(self):
        """
        function to generate postfix expression from infix expression
        """
        postfix = []

        for i in range(len(self.expression)):
            if self._is_operand(self.expression[i]):
                postfix.append(self.expression[i])
            elif self.expression[i] == '(':
                self.my_stack.push(self.expression[i])
            elif self.expression[i] == ')':
                top_operator = self.my_stack.pop()
                while not self.my_stack.is_empty() and top_operator != '(':
                    postfix.append(top_operator)
                    top_operator = self.my_stack.pop()
            else:
                while not self.my_stack.is_empty() and self._precedence(self.expression[i]) <= self._precedence(
                        self.my_stack.peek()):
                    postfix.append(self.my_stack.pop())
                self.my_stack.push(self.expression[i])

        while not self.my_stack.is_empty():
            postfix.append(self.my_stack.pop())
        return ' '.join(postfix)

    @staticmethod
    def get_code():
        """
        returns the code of the current class
        """
        return inspect.getsource(InfixToPostfix)

・return True if ord(char) in [ord(c) for c in ascii_letters] else False
この書き方を初めて知った。

・ord()............ord()とはPythonの組み込み関数。1文字のユニコード文字を表す文字列に対し、その文字のユニコードポイントを表す整数を返す。この反対がchr()らしい。

ユニコードポイントとは、世界中の文字にポイントが割り当てられていて、この割り振られている番号のこと。


今日も昨日と同じです。ありがとうございます!
pygorithm/stack.py at master · OmkarPathak/pygorithm · GitHub

Day7 stack

class Stack(object):
    """
    Stack object
    """
    def __init__(self, limit=10):
        """
        :param limit: the stack size
        """
        self.stack = []
        self.limit = limit

    def __str__(self):
        return ' '.join([str(i) for i in self.stack])

    def push(self, data):
        """
        pushes an item into the stack
        returns -1 if the stack is empty
        """
        if len(self.stack) >= self.limit:
            # indicates stack overflow
            return -1       
        else:
            self.stack.append(data)

    def pop(self):
        """
        pops the topmost item from the stack
        returns -1 if the stack is empty
        """
        if len(self.stack) <= 0:
            # indicates stack underflow
            return -1       
        else:
            return self.stack.pop()

    def peek(self):
        """
        returns the topmost element of the stack
        returns -1 if the stack is empty
        """
        if len(self.stack) <= 0:
            # stack underflow
            return -1       
        else:
            return self.stack[len(self.stack) - 1]

    def is_empty(self):
        """
        checks if the stack is empty
        returns boolean value, True or False
        """
        return self.size() == 0

    def size(self):
        """
        returns the current size of the stack
        """
        return len(self.stack)

    @staticmethod
    def get_code():
        """
        returns the code for current class
        """
        return inspect.getsource(Stack)


・__init__(self, limit=10)........上限値らしい。

・self.size().........おそらくデータサイズのこと。

・@staticmethod..........デコレータ だって。インスタンス化しなくても呼び出し可能な関数のことらしいがよくわからんが、やりながら覚えよう。
【Python入門】デコレータの使い方をわかりやすく解説! | 侍エンジニアブログ

・inspect.getsource(Stack)...................活動中のオブジェクトから情報を取得する関数を定義している(inspect)。オブジェクトのソースコードを返す(.getsource)


明日もこちらをやってみます。ありがとうございます!
pygorithm/stack.py at master · OmkarPathak/pygorithm · GitHub

Day6 おもろくなってきたぜい!!

How To Become A Hacker: Japanese

class IndexView(LoginRequiredMixin, View):
    def get(self, request, *args, **kwargs):
        queryset = Book.objects.select_related('publisher').prefetch_related('authors').order_by('publish_date')
        keyword = request.GET.get('keyword')
        if keyword:
            queryset = queryset.filter(
                Q(title__icontains=keyword) | Q(description__icontains=keyword)
            )
        context = {
            'keyword': keyword,
            'book_list': queryset,
        }
        return render(request, 'shop/book_list.html', context)


index = IndexView.as_view()


class DetailView(LoginRequiredMixin, View):
    def get(self, request, book_id, *args, **kwargs):
        book = Book.objects.get(pk=book_id)
        context = {
            'book': book,
            'stripe_pub_key': settings.STRIPE_PUBLISHABLE_KEY,
        }
        return render(request, 'shop/book_detail.html', context)


detail = DetailView.as_view()

・.select_related関数/.prefetch_related関数
クエリを飛ばしすぎることを防ぐためにするらしい。ちょっとまだわからないからざっくりと覚えておく。
上記二つに関しては以下参照。
Django ORM の select_related, prefetch_related の挙動を詳しく調べてみた - akiyoko blog


・Qオブジェクト.......検索フォームをつくる時に使用することが多いらしい。モデルのデータの中からOR検索する時に使われるらしい。
Qオブジェクトの使い方を解説【具体的なコード付き】 - code for Django

class CheckoutView(LoginRequiredMixin, View):
    def post(self, request, *args, **kwargs):
        import time
        start_time = time.time()
        logger.info("User({}) posted the form.".format(request.user.id))

        stripe.api_key = settings.STRIPE_API_KEY
        token = request.POST['stripeToken']
        item_id = request.POST['item_id']
        book = get_object_or_404(Book, pk=item_id)

        try:
            charge = stripe.Charge.create(
                amount=book.price,
                currency='jpy',
                source=token,
                description=book.title,
            )
        except stripe.error.CardError as e:
            # The card has been declined
            return render(request, 'error.html', {
                'message': "Your payment cannot be completed. The card has been declined.",
            })

        logger.info("Charge[{}] created successfully.".format(charge.id))
        messages.info(request, "Your payment has been completed successfully.")
        logger.debug("Finished in {:.2f} secs.".format(time.time() - start_time))
        return render(request, 'shop/complete.html', {
            'charge': charge,
        })


checkout = CheckoutView.as_view()


・import time を関数の中に書けることを初めて知った。
・time.time()....現在時刻を取得。returnの単位が秒かつ浮動小数点。


まず、ログとは、プログラムの実行中に起こった出来事の記録。loggingとはログを出力すること。
そして、ログレベルというものがある。そのうちのものがinfoとdebug。

・logger.info()....正常動作の記録
・logger.debug()........動作確認などデバッグの記録
【Python入門】loggingモジュールで処理の記録を残してみよう! | 侍エンジニアブログ

・try: except文......pythonの例外処理や。これは、知ってたけど、今回の写経回初めてで重要だと思ったからログを残しておこうと思った。


今日のソースコード元は以下です。ありがとうございます!
django-book-mysite-sample/views.py at master · akiyoko/django-book-mysite-sample · GitHub

今日は以上!

Day5

# -*- coding:utf-8 -*-
#! usr/bin/env python3.7

import requests
import json
import zipfile
import glob
from bs4 import BeautifulSoup


# 書類一覧APIのエンドポイント
url = "https://disclosure.edinet-fsa.go.jp/api/v1/documents.json"

# 書類一覧APIのリクエストパラメータ
params = {
  "date" : '2014-03-18'
  "type" : 2
}

# 書類一覧APIの呼び出し
res = requests.get(url, params=params, verify=False)

# resultデータ取得
res_text = json.loads(res.text)
results = res_text["results"]

# 決算データに絞る
kessan = []
for result in results:
    if result['docDescription'] is not None:
        if '四半期' in result['docDescription']:
            kessan.append(result)


# zipファイルの取得
docid = kessan[0]['docID']
url = 'https://disclosure.edinet-fsa.go.jp/api/v1/documents/' + docid
pramas = {
  "type" : 1
}
res = requests.get(url, params=params, verify=False)

# ファイルへの出力
filename = docid + ".zip"
if res.status_code == 200:
    with open(filename, 'wb') as f:
        for chunk in res.iter_content(chunk_size=1024):
            f.write(chunk)


# zipファイル解凍
with zipfile.ZipFile(filename) as existing_zip:
    existing_zip.extractall(docid)


# 対象htmの取得
filepath = docid + '/XBRL/PublicDoc'
files = glob.glob(filepath + '*.htm')
files = sorted(files)
target_file = files[1]

with open(target_file, encoding='utf-8') as f:
    html = f.read()

# htmデータの取得
soup = BeautifulSoup(html, 'html.parser')

tag_p = soup.find_all('p')
for p in tag_p:
    print(p.text)

・エンドポイント.....ネットワークにつながっているパソコンとかサーバとかスマホのこと。ネットワークにつながっている端末のこと。ネットワークの端っこ、末端にいる端末。エンドポイントと言ったら、クライアント側のコンピュータとサーバーも含むよ。

・params の verify=FalseとはSSL証明書の検証に使うらしい!verify=FalseだとSSLErrorになるのを無視するよん。

JSON.....JSONとはjavascript objects notionのことでテキストベースのデータフォーマットのこと。Javascriptのオブジェクト表記構文のサブセットとなっている。XMLと比べると簡潔に構造化されたデータを記述することができるため、記述が容易で人間が理解しやすいデータフォーマットらしい。
JSONってなにもの? | Think IT(シンクイット)

json.loads(params)......JSON形式の文字列を辞書に変換するために使う。

・if res.status_code == 200........res.status_codeとはHTTPステータスコードのことで、100番台が処理中、200番台が成功、300番台がリダイレクト、400番台クライアントエラー、500番台がサーバーエラーのこと。
PythonでWebページを取得できたかどうかのエラーチェックと安全な中止の仕方

・chunk......ひとまとまりのデータのこと。

・zipfile.ZipFile(filename)..........ZipFileオブジェクトを通して、zipファイルにアクセスする

・.extractall...............これで解凍される。

・files = glob.glob(filepath + '*.htm')........glob関数を使うことで特定のパターンにマッチするファイルを取得することができる。特定のファイルの「かたまり」を取得するイメージ。
【初心者向け】Pythonのglobを徹底解説!正規表現の書き方も説明 | TechTeacher Blog


・files = sorted(files).......まずソートとは、既存のリストの要素を並び替えるもので、これはリストに対してしか使えない。そして、sorted関数はリスト以外にも、タプル、辞書、集合に使うことができる。
Pythonでリストをソートするsortとsortedの違い | note.nkmk.me
Pythonのsortメソッドとsorted関数の使い方の詳しい解説 | HEADBOOST


本日のコードはこれ!ありがとうございました!
【Python】企業の決算情報をEDINET APIで取得する | 無次元日記

Day4

# -*- coding:utf-8 -*-
#! usr/bin/env python3.7
from datetime import date
import os
import sys
import requests
import json
from zipfile import ZipFile
import pandas as pd
import glob

# XBRLをpython形式に変換するライブラリのフォルダパス
sys.path.append(r'任意のフォルダパス')
from xbrl_proc import read_xbrl_from_zip
import urllib3
from urllib3.exceptions import InsecureRequestWarning
urllib3.disable_warnings(InsecureRequestWarning)

def get_list(start, end, company_list):
    """指定した期間、報告書種類、会社で報告書を取得し、取得したファイルのパスの辞書を返す"""
    #取得期間の日付リストを作成
    day_term = [start + timedelta(days=i) for i in range((end - start).days)]

    # データ抜出時に使用する、有価証券報告書および四半期報告書のコードの設定
    ordinance_code = "010"
    form_code_quart = "043000" # 四半期報告書
    form_code_securities = "030000" # 有価証券報告書

    # EDINETのAPIで、書類一覧を取得し、各日ごとに必要な書類の項目の抜き出し
    quart_list = []  # 四半期報告書のリスト 
    securities_list = []  # 有価証券報告書のリスト

    print('EDINETへのアクセスを開始')
    for i,day in enumerate(day_term):
        url = "https://disclosure.edinet-fsa.go.jp/api/v1/documents.json"
        params = {"data": day, "type": 2}

        # 進捗表示
        if i % 50 == 0:
            print('{i}日目:{day}を開始'.format(i, day))

        # EDINETからの1日の書類一覧を取得
        res = requests.get(url, params=params, verify=False)

        # 必要な書類の項目の抜き出し
        if res.ok:
            json_data = res.json()

            for data in json_data['results']:
                # 指定した会社の、指定した書類の抜き出し
                if data['ordinanceCode'] == ordinance_code and data['formCode'] == form_code_quart and data['filerName'].replace('株式会社', '') in company_list:
                    quart_list.append(data)
                elif data['ordinanceCode'] == ordinance_code and data['formCode'] == form_code_securities and data['filerName'].replace('株式会社', '') in company_list:
                    securities_list.append(data)

        else:
             print('アクセス失敗かも{day}'.format(day))

    list_dic = {'四半期報告書':quart_list, '有価証券報告書':securities_list}

    return list_dic
day_term = [start + timedelta(days=i) for i in range((end - start).days)]

・リスト内包表記

list = [function(i) for i in range(params)]

・繰り返し処理

for i,day in enumerate(day_term)

enumerate関数がここで使えるのは今日の学び。

・辞書

params = {"data": day, "type": 2}

・フォーマット

print('{i}日目:{day}を開始'.format(i, day))

json
json()は何を表すのか。

 json_data = res.json()


・replaceと、繰り返し処理のデータ表示のしかたの例

for sth in data['result']
  sth['content_in_result'] == sth[''].replace(' ',' ')


・リストを辞書に含めることができる

quart_list = []
securities_list = []
list_dic = {'四半期報告書':quart_list, '有価証券報告書':securities_list}
def get_zip(list_dic, quart_dir_path, securities_dir_path):
    """取得したいデータをzipファイルで取得してファイルパスのリストを返す"""
    dir_path_dic = {'四半期報告書':quart_dir_path, '有価証券報告書':securities_dir_path}
    file_path_dic = {'四半期報告書':[], '有価証券報告書':[]}  # ダウンロードした有価証券報告書のパスを格納する辞書

    for key in list_dic.keys():

        # 既にzipをDLしている場合のため、既存んおdocIDリストを取得
        files = os.listdir(dir_path_dic[key])
        existing_docID_list = [file.split('.')[0].split('_')[1] for file in files if os.path.isfile(os.path.join(dir_path_dic[key], file))]

        print('{key}ファイルのDLを開始'.format(key))
        for i, doc in enumerate(list_dic[key]):
            # zipファイルパスのリストの作成
            file_name = doc['filerName'].replace('株式会社', '') + '_' + doc['docID']
            file_path = os.path.join(dir_path_dic[key], file_name + ".zip")
            file_path_dic[key].append(file_path)

            # 所有していないファイルの場合はDLを行う
            if doc['docID'] not in existing_docID_list:
                # ファイルを取得
                url_zip = "https://disclosure.edinet-fsa.go.jp/api/v1/documents/" + doc['docID']
                params_zip = {"type":1}

                # 進捗表示
                if i % 100 == 0:
                    print('{i}ファイル目を開始'.format(i))

                # データのDL
                res_zip = requests.get(url_zip, params=params_zip, verify=False, stream=True)

                # zipとして保存
                if res_zip.status_code == 200:
                    with open(file_path, 'wb') as f:
                        for chunk in res_zip.iter_content(chunk_size=1024):
                            if chunk:
                                f.write(chunk)
                                f.flush()

        return file_path_dic

・keys()とは、おそらく、辞書のkeyだな!

for key in file_path_dic.keys():


・これこれ、俺が気になったコードは。まず、リスト内包表記、でifの条件分岐をリストに入れることができるのか。
・split("第一引数"、"第二引数")があって、第一引数にはどこで区切るか。第二引数には初めから数えてどのくらい区切るか、を指定する。で[0]は一番初めのもの。それをさらにsplitして[1]から表示する。というような気がする。わからんが。わかったら直していこう!

existing_docID_list = [file.split('.')[0].split('_')[1] for file in files if os.path.isfile(os.path.join(dir_path_dic[key], file))]

list = [something for x in Xs if sth]

次のコードへ。

def zip_to_df(file_path_dic):
  """ダウンロードしたzipをdfに変換して各会社のdicにして返す"""

  all_df_dic = {}
  for key in file_path_dic.keys():
      print('{key}データの変換を開始'.format(key))
      df_dic = {}
      for i, company_zip in enumerate(file_path_dic[key]):
          # 進捗表示
          if i % 100 == 0:
              print('{i}ファイル目を開始'.format(i))
          company_name = os.path.splittext(os.path.basename(company_zip))[0].split('_')[0]
          doc_name = ps.path.splittext(os.path.basename(company_zip))[0].split('_')[1]

          if company_name not in df_dic: # 会社が辞書中に存在しない場合
              df_dic[company_name] = {}
              df_dic[company_name] = read_xbrl_from_zip(company_zip)[0]
          elif company_name in df_dic: # 会社が辞書中に存在する場合
              df_dic[company_name] = pd.concat( [df_dic[company_name],read_xbrl_from_zip(company_zip)[0]])

      all_df_dic[key] = df_dic

  return all_df_dic

# 会社のリストを読み込み
company_list = ['ホウスイ','カネコ種苗']

# 取得期間の設定:直近n日分
delta_day = 800
end = date.today()
start = date.today() - timedelta(days=delta_day)

# ダウンロードしたデータのフォルダパス
quart_dir_path = r'四半期報告書を保存する任意のフォルダパス'
securities_dir_path = r'有価証券報告書を保存する任意のフォルダパス'

# XBRLデータの取得
list_dic = get_list(start,end,company_list)
file_path_dic = get_zip(list_dic, quart_dir_path, securities_dir_path)

# XBRLからデータ形式を変換
df_dic = zip_to_df(file_path_list)

# 営業利益部分抽出
quart = all_df_dic['四半期報告書']['カネコ種苗']
quart_oi = quart[(quart['tag'] == 'OperatingIncome') & (quart['context'] == 'CurrentYTDDuration')]
quart_oi[['第N期', '終了日', '値']].sort_values('終了日')
quart_oi = quart_oi.rename({'終了日':'Date', '値':'OperatingIncome'}, axis=1).reset_index(drop=True)
quart_oi['type'] = '四半期報告書'

securities = all_df_dic['四半期報告書']['カネコ種苗']
securities_oi = securities[(securities['tag'] == 'OperatingIncome') & (securities['context'] == 'CurrentYTDDuration')]
securities_oi[['第N期', '終了日', '値']].sort_values('終了日')
securities_oi = securities_oi.rename({'終了日':'Date', '値':'OperatingIncome'}, axis=1).reset_index(drop=True)
securities_oi['type'] = '四半期報告書'

oi_df = pd.concat([quart_oi, securities_oi]).sort_values('Date').reset_index(drop=True)

# 各四半期ごとの営業利益や、前年同期比の売上高営業利益率の変化を計算
oi_df['tempOperatingIncome'] = oi_df['OperatingIncome'] - oi_df['OperatingIncome'].shift(1)
oi_df['CalcOperatingIncome'] = oi_df['OperatingIncome'].mask(oi_df['第N期'] == 0, oi_df['tempOperatingIncome'])
oi_df['CalcOperatingIncome'] = oi_df['CalcOperatingIncome'].mask(oi_df['第N期'] == 3, oi_df['tempOperatingIncome'])
oi_df['CalcOperatingIncome'] = oi_df['CalcOperatingIncome'].mask(oi_df['第N期'] == 2, oi_df['tempOperatingIncome'])
oi_df['CalcOperatingIncome_YoYchangerate'] = oi_df['CalcOperatingIncome'] / oi_df['CalcOperatingIncome'].shift(4)

# 棒グラフで可視化
oi_df.plot.bar(x='Date', y='CalcOperatingIncome', rot=45)

oi_df.plot.bar(x='Date', y='CalcOperatingIncome_YoYchangerate', rot=45)

明日からは短めのコードをかくこと。
その中で一番ためになったコードを説明してみることにする。

以上!



ソースコード元は
pythonで財務分析:EDINETから四半期ごとの情報を取得する | kiseno-log

import requestsができない場合、sysが探索する範囲について

ImportError: No module named requests というエラーがでた。

まず、pip list でインストールされているか確認

(venv)$ pip3 list

次に、pip3 show でインストール先を確認

(venv)$pip3 show

これでインストール先が出る。

次に、インタラクティブモードでpythonの探索範囲がどこになっているか確認する。

(venv)$ python3.7

>>>import sys
>>>print(sys.version); print(sys.path)
[' ', '------------', '-----------------']  #と何個かリストで返されてくる

そして、リストにインストール先が表示されていなかったら、pathを追加する

>>>sys.path.append('書かれていたインストール先のpath')

それでも直らなかったから、一番初めにかくimport requests の上にimport sysを持ってきたら直った。

つまりsys、requestsの順にしたらErrorが出なくなった。なぜかはわからんが。だんだんやっていくうちにわかってくるだろう。

以上