[Python] AndroidLint result to CSV

최근 개발 도구를 JetBrain 라인의 제품을 사용하고 있습니다.

Android는 AndroidStudio를 이용해서 하고 Java는 IntelliJ를 이용하고 Python은 PyCharm을 이용해서 하고 있습니다.

같은 회사의 제품을 사용하니 단축키를 동일하게 할 수 있어 좋습니다.
물론 Android Studio에서 Eclipse 단축키를 제공하기는 하지만 저는 그냥 단축키를 바꾸는 편을 선호 합니다.

예전에 PC방에 가도 단축키를 모두 바꿔서 적응하면 게임방을 옮길때마다 변경해줘야 하는 불편함이 있어서 몸에 벤 방식이 기본을 그대로 사용하고 있습니다. (많이 불편하면 변경을 합니다.)

잡설은 빼고… 어쨌든 정적 분석도구를 이용해서 분석한 후 Export를 하려고 하니 HTML, XML 두가지 방식을 지원합니다. XML로 저장 후 Excel로 읽을려고 하니 안될꺼 같아. 내용만 뽑아 CSV형태로 저장하는 방식으로 했습니다.

[Problem]
AndroidStudio에서 Lint를 이용해 분석된 XML 결과를 Excel로 보고싶다.

[Solution]
XML을 파싱해서 분석 후 CSV 형태로 저장하자.

[Review]
너무 간단하게 파싱이 되서 당황했습니다. 지금은 Java로 작성하는게 훨씬 편한거 같은데 python이 익숙해지면 훨씬 쉽게 작성할 수 있을 것 같습니다.
그리고 Linux에서는 python을 기본으로 제공해주니 별도의 설치도 필요 없습니다.(Python3는 별도로 설치해야함)

"""
# AndroidLintParser.py
# Author: coozplz@gmail.com
# Date: 2014. 08. 06
# Desc: Parse android stuidio's code analyze result(xml) to csv.
# usage: AndroidLintParser.py [-h] input output
    positional arguments:
      input       Android lint export path
      output      Output csv path
# Input:
<problems>
    <problem>
        <file>....</file>
        <line>9</line>
        <module>...</module>
        <package>...</package>
        <entry_point TYPE="field" FQNAME="..." />
        <problem_class severity="WARNING" attribute_key="WARNING_ATTRIBUTES">
                                    Declaration can have final modifier
        </problem_class>
        <hints />
        <description>Declaration can have final modifier</description>
  </problem>
</problems>
# Output:
=============================================================
    SEVERITY, MODULE, PACKAGE, FILE_NAME, LINE, DESC
=============================================================
"""
from xml.etree.ElementTree import parse
from xml.sax.saxutils import unescape
from os import listdir
from os import path
import argparse
import sys
'''
# 입력 파라미터를 설정한다.
'''
parser = argparse.ArgumentParser()
parser.add_argument("input", help="Android lint export path", type=str)
parser.add_argument("output", help="Output csv path", type=str)
args = parser.parse_args()

# 사용자가 input path의 마지막에 '/' 를 붙이지 않을 수도 있기 임의로 붙여준다.
# 두개를 붙여도 상관은 없다
XML_INPUT = args.input+"/"
CSV_OUTPUT = args.output


def parse_android_lint_to_csv(element):
    """
    AndroidLint 의 Problem Tag 를 분석하여 원하는 값을 추출한다.
    :param element: Problem Element
    :return: output String
    """
    filename = element.findtext("file")
    filename = filename[filename.rfind("/")+1:]
    entry_point = element.find("entry_point").get("FQNAME")
    entry_point = entry_point.split(' ')
    if len(entry_point) > 1:
        entry_point = entry_point[1]
    line = element.findtext("line")
    module = element.findtext("module")
    package = element.findtext("package")
    severity = element.find("problem_class").get("severity")
    desc = unescape(element.findtext("description"))
    if severity == "TYPO" or severity is "None":
        return
    return "{0},{1},{2},{3},{4},{5},{6}\n".format(severity,
                                                  module,
                                                  package,
                                                  filename,
                                                  entry_point,
                                                  line,
                                                  desc)


'''
#Main
'''
try:

    f = open(CSV_OUTPUT, "w")
except IOError:
    print('cannot open', CSV_OUTPUT)
else:
    try:
        # 제외 파일명 저장용 배열
        exceptFiles = []
        # 제외된 파일의 개수
        exceptFileCount = 0
        # 총 파일의 개수
        count = 0
        for iterFileName in listdir(XML_INPUT):
            count += 1
            if iterFileName.lower().find(".xml") < 0:
                '''
                # 파일명이 Xml 형식이 아닌것은 제외 목록에 추가하고 처리하지 않는다.
                '''
                exceptFileCount += 1
                exceptFiles.append(iterFileName)
                continue
            xmlDoc = parse(XML_INPUT + iterFileName)
            problems = xmlDoc.getroot()
            for parent in problems:
                data = parse_android_lint_to_csv(parent)
                if data:
                    # 데이터가 있는 경우에만 파일에 쓴다
                    f.write(data)
    except IOError as ioE:
        print("I/O error: {0}".format(ioE))
    except ValueError as ve:
        print("Could not convert data: {0}".format(ve))
    except:
        print("UnExpected error", sys.exc_info()[0])
        raise
finally:
    # 파일은 반드시 닫는다
    f.close()


# 결과를 출력
print("TOTAL={0}".format(count))
print("OUTPUT_FILE_NAME={0}".format(CSV_OUTPUT))
print("OUTPUT_FILE_SIZE=%0.2f KBytes" % (path.getsize(CSV_OUTPUT) / 1024))
if exceptFileCount > 0:
    print("EXCEPT={0}, EXCEPT_FILES={1}".format(exceptFileCount,
                                                exceptFiles))

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s