Web/Flask-python

Flask에서 MD 문서 랜더링 + 디렉토리 하위경로 dict로 파싱해서 Jinja에서 출력하기

디렉토리 하위 경로를 dict 형태로 파싱하기

최종 목표 : md 문서가 있는 디렉토리 경로를 읽어와 웹페이지에서 마크다운 문서를 랜더하는것.

디렉토리 받아서 하위 경로 DICT + list 형태로 자동 파싱해주는 코드 만들기

def readdir(dir):
    tdir = {}
    ret = {}
    tdir['dir'] = []
    for file,key in dir.items() :
        if key == None :
            tdir['dir'].append(file)
        else :
            ret = readdir(key)
            ret['fname'] = file
            tdir['dir'].append(ret)
    return tdir

def dirparser(rootdir):
    dir = {}
    res = os.walk(rootdir)

    rootdir = rootdir.rstrip(os.sep)
    start = rootdir.rfind(os.sep) + 1

    for path, dirs, files in res:
        folders = path[start:].split(os.sep)
        subdir = dict.fromkeys(files)
        parent = reduce(dict.get, folders[:-1], dir)
        # print(type(subdir))
        parent[folders[-1]] = subdir

    return readdir(dir)

rdir = "dir"
print(dirparser(rdir))

{
    'dir': [
        {'dir': 
            ['doctest1.md', 'doctest1_copy.md', 
            {'dir': ['서포트+로그+타입 copy.md', '서포트+로그+타입.md'], 'fname': 'folder1'}, 
            {'dir': ['서포트+로그+타입 copy.md', '서포트+로그+타입.md'], 'fname': 'folder13'}, 
            {'dir': ['doctest1_copy_2.md', 
                {'dir': [
                        'doctest1_copy_3.md', 
                            {'dir': [], 'fname': 'f1'}, 
                            {'dir': [], 'fname': 'f2'}
                        ], 
                        'fname': 'folder2_1'}
                    ],
                'fname': 'folder2'}            
            ],             
            'fname': 'docs'}
        ]
}

None이 아닌 경우 -> dir.items() 에 의해 k1,k2로 구분되어 반복문 돌아감
k2가 None인 경우 -> k1을 출력
아닌 경우 -> dir.items()에 의해 ~
None인 경우-> 이름을 출력

파싱 된다. 이제 해야하는것

None인 경우 - 'name' : 'fname + 들어갈때도 파일이름 저장은 공통적으로 필요함.
아닌 경우 - dir : 'dict' 형식으로 세팅 필요함.

딕셔너리 반환하는 식으로 인자 체크 가능?

 [  
name : file  
dir : [  
{  
name : file  
dir : {}  
},  
 ]

 ]

저 name과 dir가 들어있는 딕셔너리가 통째로 반환되어야함.
맨 마지막의 dir은 (None을 만나는 경우) -> 파일 이름만 반환되고 dir는 비어있을(None이 될) 예정.
그리고 어쨋든 None이 아니면 현재 디렉토리 구조가 있다는 이야기 -> 폴더 이름을 만들어야한다.
== None인 경우 파일이라는 뜻.
현재 디렉토리 탐색이 끝났는데 dir이 비어있는경우
-> 더 내려갈 하위 디렉토리가 없음. 현재 디렉토리 이름이 현재 dict의 fname 요소가 되어야함.
반환받은 요소를 검사할 필요가 있나? -> 반환받은 dir 요소의 dict가 비어있는 경우, 해당 dir요소는 최 하위 디렉토리라는 의미가 되므로, 가지고 있는 이름을 fname으로 지정해줘야 한다. -> 아님.
그냥 요소를 반환받을때 반환받은 dict에 fame 추가해주면됨.;; 간단하게 생각하자


jinja에서의 결과물 출력 방식

'fname' : 'foldername' ,
'dir' : [ 'filename',
               {'dir' : [] , 'fname' : 'f1' },
                    {'dir' : [] , 'fname' : 'f2' },
    ]
  1. 폴더이름은 디렉토리당 한번씩만 출력됨
  2. dir 내부 요소에 폴더랑 파일이 모두 포함됨.
  3. 요소 타입이 string일 경우 파일명 → 그냥 출력하면됨
  4. string이 아닌경우 디렉토리 → 반복문 진행
  5. fname이 없는 경우 → 이 경우도 그냥 출력
{% for d1 in dir['dir'] %}
        name1 : {{ d1['fname'] }} <br>
        {% for d2 in d1['dir'] %}
            {% if d2['fname'] %}
                name2 : {{ d2['fname'] }} <br>
            {% else %}
                file2 : {{ d2 }} <br>
            {% endif %}

            {% for d3 in d2['dir'] %}
                {% if d3['fname'] %}
                    name3 : {{ d3['fname'] }} <br>
                {% else %}
                    file3 : {{ d3 }} <br>
                {% endif %}

                {% for d4 in d3['dir'] %}
                    {% if d4['fname'] %}
                        name4 : {{ d4['fname'] }} <br>
                    {% else %}
                        file4 : {{ d4 }} <br>
                    {% endif %}
                {% endfor %}

            {% endfor %}

        {% endfor %}

{% endfor %}

파일을 불러오기 위해서는 디렉토리 경로도 표기해줘야한다.

이건 파싱함수 코드를 좀 바꿔야할듯. 디렉토리를 내려갈 때 마다 상위 디렉토리 이름을 누적 표기하는 식으로 해보자.

{% for d1 in dir['dir'] %}
                name1 : {{ d1['fname'] }} <br>
                {% for d2 in d1['dir'] %}
                    {% set dname1 = d1['fname']+"/" %}
                    {% if d2['fname'] %}
                        name2 : {{ dname1+d2['fname'] }} <br>
                    {% else %}
                        file2 : {{ dname1+d2 }} <br>
                    {% endif %}

                    {% for d3 in d2['dir'] %}
                        {% set dname2 = dname1+d2['fname']+"/" %}
                        {% if d3['fname'] %}
                            name3 : {{ dname2+d3['fname'] }} <br>
                        {% else %}
                            file3 : {{ dname2+d3 }} <br>
                        {% endif %}

                        {% for d4 in d3['dir'] %}
                            {% set dname3 = dname2+d3['fname']+"/" %}
                            {% if d4['fname'] %}
                                name4 : {{ dname3+d4['fname'] }} <br>
                            {% else %}
                                file4 : {{ dname3+d4 }} <br>
                            {% endif %}
                        {% endfor %}

                    {% endfor %}

                {% endfor %}

        {% endfor %}

혹은 경우에 따라 파일에만 전체 디렉토리가 나오게 할수도 있다.

{% for d1 in dir['dir'] %}
                name1 : {{ d1['fname'] }} <br>
                {% for d2 in d1['dir'] %}
                    {% set dname1 = d1['fname']+"/" %}
                    {% if d2['fname'] %}
                        name2 : {{ d2['fname'] }} <br>
                    {% else %}
                        file2 : {{ dname1+d2 }} <br>
                    {% endif %}

                    {% for d3 in d2['dir'] %}
                        {% set dname2 = dname1+d2['fname']+"/" %}
                        {% if d3['fname'] %}
                            name3 : {{ d3['fname'] }} <br>
                        {% else %}
                            file3 : {{ dname2+d3 }} <br>
                        {% endif %}

                        {% for d4 in d3['dir'] %}
                            {% set dname3 = dname2+d3['fname']+"/" %}
                            {% if d4['fname'] %}
                                name4 : {{ d4['fname'] }} <br>
                            {% else %}
                                file4 : {{ dname3+d4 }} <br>
                            {% endif %}
                        {% endfor %}

                    {% endfor %}

                {% endfor %}

        {% endfor %}


flaks에서 markdown 문서 출력하기

pip install Flask-markdown 

flask-markdown 패키지 설치

이후 init 파일에 flask app 추가

app = Flask(__name__,static_url_path='/static')
~~~~
Markdown(app,extensions=['nl2br','fenced_code'])

app 추가되었으면 view 문서에서 아래와 같이 작성

mf = open(rootpath+'\\'+fname,"r",encoding='UTF8')
md = mf.read()
#md = "## Your Markdown Here "
return render_template('docbase.html',dir=r,doc=fname,md=md)

랜더할때 그냥 인자로 읽어들인 마크다운 텍스트 내용을 그대로 넘겨주면 된다.

<div class="container-fluid" style="margin-left:25%"> 
        <div>
            <h6>file path : {{ doc }}</h6>
            {% if md %}
            <div>{{ md|markdown }}</div>

            {% else %}

            {% endif %}
        </div>

    </div>

html 템플릿 파일은 받아온 내용을 그대로 markdown 형태로 출력하면 됨.

### MD TEST DOC

1. 마크다운 테스트 문서입니다
2. 마크다운 텍스트?

- 마크당우우우우운
- 마아아읔 다운
markdown code highlights test

그러면 저렇게 나온다.


파일 오픈시 'cp949' 인코딩 에러

"UnicodeDecodeError: 'cp949' codec can't decode byte 0xe2 in position 6987: illegal multibyte sequence"

파일 입출력시 위와같은 에러가 날떄가 있는데, 파일을 읽을때 인코딩이 잘못되어 발생하는 에러다.

mf = open(rootpath+'\\'+fname,"r",**encoding='UTF8'**)

encoding='UTF8' 로 인코딩 명시해주면 해결