Code Mining. Могут ли аналитики читать код?

4e401fe529c52be143897841ce640416.jpg

Привет, Хабр! На связи участник профессионального сообщества NTA Губин Никита.

Введение

Code mining — это процесс анализа и извлечения информации из исходного кода для получения полезных данных. Аналитики, имея базовые компетенции в разработке, могут использовать их как дополнительный источник информации для улучшения процессов. Инструмент, о котором я расскажу в посте, разделяет код на логические блоки, что позволит улучшить взаимодействие DS‑специалистов и аналитиков.

Аналитики и код

Почему же аналитикам трудно самостоятельно анализировать исходный код?

Выделим несколько причин:

— Незнакомые средства, в виде расширенного поиска по составам репозиториев;

— Непривычное разрозненное расположение информации, которая распределена по умозрительным и непонятным для аналитика признакам;

— Возможное несоответствие документации с исходным кодом.

Решение

В качестве решения было разработано приложение, которое парсит все файлы проекта с кодом в один excel файл, поделенный на логические блоки:

Пример работы

Пример работы

Преимущество данного решения:

— Привычный инструмент. Excel — один из основных инструментов аналитика;

— Код проекта, состоящий из нескольких файлов, собирается в одном месте;

— За счет структуры файла, у аналитика появляются широкие возможности по фильтрации, анализу и поиску необходимой информации.

Реализация

Код написан на Python и предназначен для разбора кода так же на Python.

В посте будут разобраны ключевые моменты. Полный код доступен по ссылке на Github:

https://github.com/AIKarasu/PyCodeToExcel/blob/main/MainCode.

Изначально, необходимо собрать файлы (.py), которые в дальнейшем будем обрабатывать:

#Метод выгрузки данных
def folder(fold,pth,filenames):
    if fold == True:
        filenames = glob(os.path.join(pth, "*.py"))
    elif fold == False:
        for root, dirs, files in os.walk(pth):
            files = [f for f in files if not f[0] == '.']
            dirs[:] = [d for d in dirs if not d[0] == '.']
            for file in files:
                if(file.endswith(".py")):
                    filenames.append(os.path.join(root,file))
    return filenames

Предусмотрено 2 варианта сбора:

— Только из указанной папки;

— Из указанной папки и всех подпапок (кроме скрытых). 

Задаем DataFrame со структурой будущего excel файла:

# Создаем DataFrame с соответствующими колонками
col = ['Стратегия', 'Путь', 'Исходная функция', 'Номер строки фрагмента', 'Тип фрагмента', 'Фрагмент']
df = pd.DataFrame(columns=col) 

Построчно обрабатываем файлы:

for i in my_code:
    counter += 1
    # Обрабатываем строку
    i = i.strip(' ')
    # Проверка для записи исходной строки
    if 'def ' not in i:
        ish += i
# Обрабатываем комментарии
if i[0] == '#':
    if per != '':
        perem()
    # Заполняем IF не в одну строку
    if y == 1:
         df.at[row_in_dataframe, 'Стратегия'] = strategy
         df.at[row_in_dataframe, 'Путь'] = path
         df.at[row_in_dataframe, 'Номер строки фрагмента'] = counter
         df.at[row_in_dataframe, 'Тип фрагмента'] = 'Условие IF'
         df.at[row_in_dataframe, 'Фрагмент'] = usl
         row_in_dataframe += 1
         usl = ''
         y = 0
    dfwrite('Комментарий')  
   # Обрабатываем пустые строки
elif i.strip('\n') == '':
     continue 

Python обладает четкой структурой, поэтому, каждая строка проверяется на условия:

# Обрабатываем переменные
elif ('=' in i) and (('if ' or 'else' or 'elif ') not in i):
      y = 0
      p = 1
      per += i
      counterP=counter

      elif (p == 1) and (('if ' or 'else' or 'elif ') not in i):
           per += i

# Обрабатываем IF с условием в одну строку
elif (('if ' or 'else' or 'elif ') in i) and (':' in i):
      if per != '':
           perem()
      if ret != '':
           reter()
      dfwrite('Условие If')
      p = 0

Но если каждую строку просто записывать в DataFrame, то в итоговом результате получим «кашу».

Элементы часто занимают более одной строки, поэтому в коде предусмотрены флаги, для записи однотипных данных в одну запись. Например, когда подряд объявляются несколько переменных:

Отобранные данные записываются в DataFrame:

#Обработка фрагментов
def dfwrite(fragm):
    global row_in_dataframe, strategy, path, counter, i, df
    df.at[row_in_dataframe,'Стратегия']=strategy
    df.at[row_in_dataframe,'Путь']=path
    df.at[row_in_dataframe,'Номер строки фрагмента']=counter
    df.at[row_in_dataframe,'Тип фрагмента']=fragm
    df.at[row_in_dataframe,'Фрагмент']=i
    row_in_dataframe+=1
    pass

Полученный результат записываем в Excel или в csv:

#Параметры и формат сохранения
def save(df,xl,pthEnd):
    if xl == True:
        df.to_excel(os.path.join(pthEnd, "CodeMine_Result.xlsx"))
    elif xl == False:
        df.to_csv(os.path.join(pthEnd, "CodeMine_Result.csv"), encoding="windows-1251", sep="~") 

Для удобства работы добавим пользовательский интерфейс:

if __name__ == "__main__":
    root = Tk()
    root.geometry('600x400')
    var1 = BooleanVar()
    var1.set(False)
    var2 = BooleanVar()
    var2.set(False)

    lbl = Label(text="Задайте параметры")
    lbl.pack()

    frame1 = Frame(borderwidth=1, relief=SOLID)
    path_label = Label(frame1,text="Введите путь")
    path_label.pack(anchor=NW)

    path_entry = Entry(frame1, width=50)
    path_entry.pack(anchor=NW)

    open_button = Button(frame1,text="Открыть проводник", command=open_dir1)
    open_button.pack(anchor=NW)

    frame1.pack(anchor=NW, fill=X, padx=5, pady=5 )

Интерфейс

Интерфейс

Путь до обрабатываемых папок выбираем через проводник:

def open_dir1():
    global path_entry,filepath1
    path_entry.delete(0, END)
    filepath1 = filedialog.askdirectory()
    path_entry.insert(0,filepath1)  

А после выполнения программы предлагаем открыть результат:

# Сохраняем полученный DataFrame
save(df, xl, pthEnd)
resultMess = askyesno(title="Разбор выполнен успешно", message="Открыть полученный файл?")
if resultMess == True:
     if xl == True:
          os.startfile(os.path.join(pthEnd, "CodeMine_Result.xlsx"))
     else:
          os.startfile(os.path.join(pthEnd, "CodeMine_Result.csv")) 

Сообщение после обработки

Сообщение после обработки

Давайте рассмотрим применение программы на примере:

Пример разбираемого кода

Пример разбираемого кода

Результатом разбора данного фрагмента будет excel файл:

Результат разбора

Результат разбора

На рисунке выше можно увидеть, какие переменные используются при работе и как они преобразовываются. А за счет фильтров можно отобрать только необходимую в данный момент информацию:

Фильтр по переменной Age

Фильтр по переменной Age

Вывод

Данная программа позволила привлечь к проверке исходного кода специалистов, ранее не сталкивающихся с подобными задачами. Полученная обратная связь была позитивной и, сейчас, система проходит активную стадию тестирования.

Очевидно, что это не конечный продукт и на основе обратной связи будут дорабатываться и внедряться различные методы визуализации и обработки данных. Например, на основе графов.

Но уже сейчас, можно дать ответ на вопрос из темы поста. Да, аналитики могут читать код. И основа, заложенная в тему данного поста, является тому подтверждением.

© Habrahabr.ru