PythonでSubversionのファイルやフォルダ情報をCSV形式で保存する

# pysvnについて
 - Subversionのpython用のAPIで、svnをインストールする必要はありません。
 - svnをラップしているそうなので、機能は一緒だと思います。
 - https://github.com/dsoprea/PySvn
 - https://pysvn.sourceforge.io/Docs/pysvn_prog_ref.html

# pysvnのインストールについて
### Windows環境
 - インストーラーでインストールできます
 - https://pysvn.sourceforge.io/downloads.html
### Mac環境
 - インストーラーはありますが、py39-python_org-pysvn-svn1142-1.9.18-2091-x86_64.dmgをインストールしようとしたところエラーになりました。Pythonのパスが「/Library/Frameworks/Python.framework/Versions/3.9/lib/python3.9/site-packages」で固定されており、pyenvを使っている筆者の環境ではインストールできませんでした。次章に記載のDockerでUbuntuを導入してインストールしました。
 - https://pysvn.sourceforge.io/downloads.html
### pipインストール
 - pipインストールはできますがバージョンが古い(2022/11/25時点 pysvn 0.1.0)ので注意して下さい。（筆者はこの古いバージョンを使って謎のエラーに悩みました。）
 - https://pypi.org/project/pysvn/
 - リファレンスには1.6以降を使えと記載されてます。
 - https://pysvn.sourceforge.io/Docs/pysvn_prog_ref.html
 - ちなみにエラーはこんな感じです、とにかく1.6以降を使って下さい
```python:python
$ python3 svnlog.py http://svn.sourceforge.jp/svnroot/simyukkuri/ "admin" "admin"
Traceback (most recent call last):
  File "/Users/mba/Downloads/pysvn-main/svnlog.py", line 32, in <module>
    logs = client.log()
  File "/Users/mba/Downloads/pysvn-main/svnlog.py", line 19, in log
    return self.client.log(self.url_or_path, discover_changed_paths=True)
TypeError: log() got an unexpected keyword argument 'discover_changed_paths'
discover_changed_pathsのパラメーターがない

discover_changed_pathsを消してみると
$ python3 svnlog.py http://svn.sourceforge.jp/svnroot/simyukkuri/ "admin" "admin"
Traceback (most recent call last):
  File "/Users/mba/Downloads/pysvn-main/svnlog.py", line 33, in <module>
    logs = client.log()
  File "/Users/mba/Downloads/pysvn-main/svnlog.py", line 20, in log
    return self.client.log(self.url_or_path)
  File "/Users/mba/.pyenv/versions/3.9.1/lib/python3.9/site-packages/pysvn/client.py", line 21, in log
    root = xml.etree.ElementTree.fromstring(data)
  File "/Users/mba/.pyenv/versions/3.9.1/lib/python3.9/xml/etree/ElementTree.py", line 1348, in XML
    return parser.close()
xml.etree.ElementTree.ParseError: no element found: line 3, column 0
pysvn.Client.logができない、詰んだ…

```


# Dockerのubutuにpysvnをインストールする
### Ubuntuのイメージを起動する
 - 20.04以降のUbutuを導入する(Python2系と3系が混在すると紛らわしいためです)
 - 20.04 LTSで完全にPython2を含まないシステムになりました。
 - https://forums.ubuntulinux.jp/viewtopic.php?id=21046
 - https://qiita.com/yasuoka_dev/items/073f7e8c7dba75993323

```bash:bash
$ docker pull ubuntu:20.04
$ docker images
REPOSITORY   TAG       IMAGE ID       CREATED       SIZE
ubuntu       20.04     680e5dfb52c7   3 weeks ago   72.8MB
$ docker run -it -d --name ubuntu ubuntu:20.04
$ docker exec -it ubuntu /bin/bash
```

### Ubuntu環境の構築
```bash:bash
$ apt update
$ apt upgrade
$ apt install build-essential libbz2-dev libdb-dev \
  libreadline-dev libffi-dev libgdbm-dev liblzma-dev \
  libncursesw5-dev libsqlite3-dev libssl-dev \
  zlib1g-dev uuid-dev tk-dev

$ apt-get install vim
$ dpkg -l vim

$ apt install python3
$ python3 -V
Python 3.8.10
$ which python3
/usr/local/bin/python3

$ pip3 install pandas
```

### Ubuntuにpysvnをインストール
 - python-svnはpython2系に対応していることに気をつけて下さい

```bash:bash
$ apt-get install python3-svn
```

# pysvnでファイル情報を取得する
### 構築した環境をテストする
 - こちらの記事[「PythonでSubversionのコミットログを解析する」](https://qiita.com/mima_ita/items/8fed6e1f29deb470764f)のコードをコピペして、構築した環境のテストと公開されているサンプルのSubversionでログインなどをテストしてみて下さい。
 - [筆者のgithubにもバックアップがあります](https://github.com/kchan7/pysvn)、cloneして下記のコマンドを実行して下さい。[svnlog.txt](https://github.com/kchan7/pysvn/blob/main/svnlog.txt)の内容がコンソールに出力されたらOKです。

```bash:bash
$ python3 svnlog.py http://svn.sourceforge.jp/svnroot/simyukkuri/ "admin" "admin"
```

### ファイル情報を取得する
 - [pysvn - Programmer's reference](https://pysvn.sourceforge.io/Docs/pysvn_prog_ref.html#pysvn_client_ls)によるとpysvn.Client.lsでリポジトリのファイルやフォルダ情報が取得できます。パラメータは下記です。
     - created_rev - pysvn.Revision - the revision of the last change
     - has_props - bool - True if the node has properties
     - kind - node_kind - one of the pysvn.node_kind values←ファイルまたはフォルダ
     - last_author - the author of the last change
     - name - string - name of the file←URL
     - size - long - size of file←バイト
     - time - float - the time of the last change←タイムスタンプ
 - file_list.pyを実行するとSubversionリポジトリのファイル一覧をfile_list.csvへ出力します。
 - kindでファイルのみに絞っています、フォルダを取得したい場合にはここを変更して下さい。
 
```python:python
import os,sys
import csv
import pandas as pd
from collections import defaultdict
from datetime import datetime
import pysvn


###env
RESULT_FILE='file_list.csv'


class SvnClient:
    def __init__(self,url_or_path,user,passwd):
        self.client = pysvn.Client()
        self.url_or_path = url_or_path
        self.user = user
        self.passwd = passwd
        self.client.callback_get_login = self.get_login

    def get_login(self, realm, username, may_save):
        return True, self.user,self.passwd, False

    def ls(self):
        return self.client.ls(self.url_or_path, recurse=True)

if __name__ == '__main__':
    ## command sample
    ## python3 svnlog.py http://svn.sourceforge.jp/svnroot/simyukkuri/ "admin" "admin"
    argvs = sys.argv
    argc = len(argvs)
    if(argc != 4):
        sys.stderr.write( 'Usage:\n python %s url_or_path user pass' % argvs[0] )
        quit()
    url_or_path = argvs[1]
    user = argvs[2]
    passwd = argvs[3]

    client = SvnClient(url_or_path,user,passwd)
    ## pysvn.Client.ls
    ## https://pysvn.sourceforge.io/Docs/pysvn_prog_ref.html#pysvn_client_ls
    ## created_rev - pysvn.Revision - the revision of the last change
    ## has_props - bool - True if the node has properties
    ## kind - node_kind - one of the pysvn.node_kind values
    ## last_author - the author of the last change
    ## name - string - name of the file
    ## size - long - size of file
    ## time - float - the time of the last change

    df = pd.DataFrame(columns = ['dir_path','file_name','file_ext','size','created_rev','has_props','kind','last_author','time'])

    repo_infos = client.ls()
    for info in repo_infos:
        if str(info.kind) == "file":
            time = datetime.fromtimestamp(info.time).strftime('%Y-%m-%d %H:%M:%S')
            info_array = info.name.split('/')
            file_name = info_array[-1]
            path, file_ext = os.path.splitext(file_name)
            row = pd.DataFrame([[info.name, file_name, file_ext, info.size, info.created_rev, info.has_props, info.kind, info.last_author, time]], columns=df.columns)
            df = pd.concat([df,row],ignore_index=True)

    df.to_csv(RESULT_FILE, encoding='utf-8', index=True)

```
 - [筆者のgithub](https://github.com/kchan7/pysvn)、cloneして下記のコマンドを実行して下さい。
```bash:bash
$ python3 file_list.py http://svn.sourceforge.jp/svnroot/simyukkuri/ "admin" "admin"
```