velog 조회수 패키지, 정렬 기능 업데이트

최더디·2022년 1월 19일
8

Velog-Hits

목록 보기
2/3
post-thumbnail
post-custom-banner

🍕 Velog Hits

📍서론

지난번 velog 조회수를 종합해서 보여주는 기능을 처음으로 개발한 이후, 작성한 블로그 글에서 가장 많은 하트와 댓글이 달렸습니다. 또한, 처음으로 제가 만든 github repository 스타✨를 받아봤습니다. 많은 관심을 주셔서 감사합니다:)

감사한 마음과 함께 지난번에는 구현하지 못했던 결과 정보를 보여주는 html 파일을 css로 꾸며 봤고, js 파일을 추가해 테이블 정렬 기능도 추가했습니다.

📍 개발 내용

개발을 진행할 때 중요한 부분만 간략히 작성한 글입니다 👻
velog-hits 사용 방법은 Github or PyPI 링크를 통해 보실 수 있습니다.

1) JS 파일 생성

정렬 기능을 넣고 싶었지만 JavaScript 를 사용해 보지 않아서 막막했는데, 오픈 소스가 존재했습니다.
다운로드 받고 js 파일을 넣어주니 쉽게 적용이 되었습니다.

사용 방법

  • js 파일 다운로드
  • HTML에 <script src="sorttable.js"></script> 추가
  • 정렬을 넣고자 하는 테이블 태그 클래스명을 “sortable” 추가
    ex) <table border="1" class="sortable">

2) CSS 파일 생성

꾸미기에 소질이 없었는데, 제가 원하는 기능만 가져와 간단하게 테이블을 꾸며주는 css 파일을 생성했습니다.

위에서 다운로드 받은 js 파일을 사용하려면 클래스 이름이 “sortable” 이여야 하기 때문에 css 에서도 통일시켜줬습니다.

/* table.css */

.sortable {
  font-size: 11pt; 
  font-family: Arial;
  border-collapse: collapse; 
  border: 1px solid silver;
}

.sortable th {
  text-align: center;
}

.sortable td, th {
  padding: 5px;
}

.sortable th:hover {
  background: silver;
  cursor: pointer;
}

.sortable tbody > tr:hover {
  background: silver;
  cursor: pointer;
}

3) HTML 변환시 테이블 클래스명 넣어주기

기존에 DataFrame을 to_html() 을 통해 테이블 형식으로 만들어줬습니다. to_html()을 사용하면 결과 html이 <html></html> 로 시작하지 않고, <table></table>로 시작합니다. 해당 부분을 수정하기 위해 아래와 같은 작업을 수행했습니다.

  • css, js 파일을 추가하기 위해 html_string 값을 생성했고, formatting 값으로 dataframe_to_html_table 값을 넣어줬습니다.
  • 후에 convert_df_to_html() 메소드에서 html_string.format() 을 통해 html_string의 {dataframe_to_html_table} 값에 to_html() 로 변환된 결과값이 들어가도록 했습니다.

또한 to_html() 에서 classes="sortable" 값을 줌으로써 HTML table 태그에 클래스명을 추가했습니다.

# convertor.py

html_string = """
<html>
  <head><title>Velog Hits Result Table</title></head>
  <link rel="stylesheet" type="text/css" href="table.css"/>
  <script src="sorttable.js"></script>
  <body>
    {dataframe_to_html_table}
  </body>
</html>
"""

class DF2HTMLConverter:
	...
	def convert_df_to_html(self, df) -> bool:
	    try:
	      with open(self.html_file_path, "w") as html_file:
	        html = df.to_html(index=False, escape=False, classes="sortable")
	        sort_table_html = html_string.format(dataframe_to_html_table=html)
	        html_file.write(sort_table_html)
	      return True
	
	    except Exception:
	      return False

수정 전 HTML 시작 부분

<table border="1" class="dataframe">
  <thead>
    <tr style="text-align: right;">
      <th>post</th>
      <th>tags</th>
      <th>comments_count</th>
      <th>likes</th>
      <th>total</th>
      <th>latest_count</th>
      <th>latest_day</th>
    </tr>
  </thead>

수정 후 HTML 시작 부분

<html>
  <head><title>Velog Hits Result Table</title></head>
  <link rel="stylesheet" type="text/css" href="table.css"/>
  <script src="sorttable.js"></script>
  <body>
    <table border="1" class="dataframe sortable">
      <thead>
        <tr style="text-align: right;">
          <th>post</th>
          <th>tags</th>
          <th>comments_count</th>
          <th>likes</th>
          <th>total</th>
          <th>latest_count</th>
          <th>latest_day</th>
        </tr>
      </thead>

4) css, js 파일은 copy2() 로 저장

모든 기능을 만들고 난 후, 결과 테이블이 저장되는 htmlhits/ 디렉토리에 css, js 파일을 어떻게 넣어줄 것인지 생각을 했습니다. 두 파일은 계속해서 똑같이 복사를 해주면 되는 것이기 때문에 COPY를 하자는 생각을 했습니다.

Python에서 제공하는 shutil 패키지를 사용해 쉽게 copy 할 수 있었습니다.

이때 velog_hits_path 값은 결과를 저장하는 htmlhits/ 디렉토리를 생성해야 하기 때문에 velog-hits 명령어를 실행하는 위치를 가져와야 합니다. 그렇기 때문에 Path.cwd() 를 사용했습니다.

반대로 copy를 하려는 css, js 파일을 패키지를 배포할 때 저장되어 하고, velog-hits 패키지 안에서 가져와야 하기 때문에 copy_file_directory_path 값은 Path(**file**).resolve().parent 를 사용했습니다.

# html/creator.py

import os
import shutil

from pathlib import Path

class CssJsCreator:
  def __init__(self) -> None:
    self.velog_hits_path = Path.cwd()
    self.copy_file_directory_path = Path(__file__).resolve().parent

  def copy_css_file(self) -> bool:
    file_name = "table.css"
    css_file_path = self.get_copy_file_path(file_name)
    destination_path = self.get_destination_file_path(file_name)

    if self.file_copy(css_file_path, destination_path):
      return True
    return False

  def copy_js_file(self) -> bool:
    file_name = "sorttable.js"
    js_file_path = self.get_copy_file_path(file_name)
    destination_path = self.get_destination_file_path(file_name)

    if self.file_copy(js_file_path, destination_path):
      return True
    return False

  def get_copy_file_path(self, file_name) -> str:
    return self.copy_file_directory_path.joinpath(file_name)

  def get_destination_file_path(self, file_name) -> str:
    return os.path.join(self.velog_hits_path, "htmlhits", file_name)

  def file_copy(self, copy_file_path, destination_path) -> bool:
    try:
      if not os.path.exists(destination_path):
          shutil.copy2(copy_file_path, destination_path)
      return True

    except:
      return False

5) 결과 테이블 칼럼명 변경

결과 HTML 을 보여줄 때 테이블 칼럼값이 영어로 되어 있는 부분을 영어 → 한글로 수정했습니다.

# convertor.py

class DF2HTMLConverter:
	... 
  def get_result_dataframe(self, df, url) -> pd.DataFrame:
		...
    df = df[["post", "tags", "comments_count", "likes", "total", "latest_count", "latest_day"]]
    df = df.rename(
        columns={
            "post": "제목",
            "tags": "태그",
            "comments_count": "댓글",
            "likes": "좋아요",
            "total": "총 방문자",
            "latest_count": "최근 방문자",
            "latest_day": "최근방문날짜"
        }
    )
    return df

6) MANIFEST.in 파일 생성

마지막으로 패키지를 배포할 때 .py 파일만 배포가 되는데, 추가적으로 ,css, .js 파일도 같이 배포가 진행이 되어야 하기 때문에 MANIFEST.in 파일을 생성했고, setup.py 에 package_data 를 추가했습니다.

# MANIFEST.in
include velog_hits/html/*.css
include velog_hits/html/*.js
# setup.py
setup(
    ...
    include_package_data = True,
    package_data={"velog-hits": ["html/*.css", "html/*.js"]}
)

이 때 include_package_data = True 값을 넣어줘야 패키지 빌드 및 배포할 때 포함이 되었습니다.

📍마무리

지난 번에 결심한 "기능을 업데이트 하자"라는 목표를 이뤄서 기분이 좋네요:)
이번 기능 업데이트로 인해 현재 사용 중이고 계셨던 분들, 새롭게 해당 기능을 사용하시려고 하시는 분들에게 많은 도움이 되면 좋겠습니다!

해당 기능에 문제가 되는 부분, 잘못된 부분, 궁금한 부분이 존재한다면 댓글 또는 이메일 보내주시면 감사하겠습니다:)

profile
focus on why
post-custom-banner

2개의 댓글

comment-user-thumbnail
2022년 1월 22일

좋은글 감사합니다!!

1개의 답글