iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 19
0

把處理天氣預報的PDF 那個檔案重構一下,改成物件的方式,去呼叫,檔案名稱叫lib/pdfWeather.py,這個就專門處理PDF的格式。
檔案結構

<projectName>/
----manage.py
----<projectName>/
--------__init__.py
--------settings.py
----main/
----venv/
----lib/
--------pdfWeather.py
--------font/kaiu.pdf
--------pdfWeather.py

lib/pdfWeather.py
將之前的程式分為兩個部份,
第一個是 call 天氣的API,然後轉換成比較簡潔的格式,再回傳這個資料。

CrawlerWeatherAPI() 局部程式碼,這裡省略了一些重複的部份,請參考前幾天寫的,在getCrawlerData 多傳了locationNameList,會多判斷要回傳的區,如果指定要回傳哪一區,就回傳全部。

class CrawlerWeatherAPI:
    def getCrawlerData(self, locationNameList=[]):
        authorization = "xxxxx"
        url = "https://opendata.cwb.gov.tw/api/v1/rest/datastore/F-D0047-061"
        res = requests.get(url, {"Authorization": authorization})
        resJson = res.json()

        dataDictList = []
        locations = resJson["records"]["locations"][0]["location"]
        for index, location in enumerate(locations):
            locationName = location["locationName"]
            # if not in list continue
            # 如果有傳就判斷,如果 沒有在這個list就continue
            if locationNameList and locationName not in locationNameList:
                continue
            weatherElements = location["weatherElement"]
            temperatureDictList = []
            aTemperatureDictList = []
            pop6HDictList = []
            wxDictList = []
            ciDictList = []

            for weatherElement in weatherElements:
                # 溫度
                if weatherElement["elementName"] == "T":
                    timeDicts = weatherElement["time"]
                    for timeDict in timeDicts:
                        temperatureDictList.append({
                            "dataTime": timeDict["dataTime"],
                            "value": timeDict["elementValue"][0]["value"],
                        })
                #  體感溫度
                if weatherElement["elementName"] == "AT":
                    ...
                #  降雨機率 百分比
                if weatherElement["elementName"] == "PoP6h":
                    ...
                #  天氣現象 Text
                if weatherElement["elementName"] == "Wx":
                    ...
                # 舒適度指數
                if weatherElement["elementName"] == "CI":
                    ...

            tempDict = {
                "locationName": locationName,
                "temperatureDictList": temperatureDictList,
                "aTemperatureDictList": aTemperatureDictList,
                "pop6HDictList": pop6HDictList,
                "wxDictList": wxDictList,
                "ciDictList": ciDictList,
            }
            dataDictList.append(tempDict)
        return dataDictList

第二部份,就是處理PDF樣式的程式,一樣也是貼局部,叫 PdfWeather(),這裡因為多加了一層lib/資料夾,執行根位置是在上一層,所以會找不到檔案,就改成絕對位置吧,不過也是用程式抓這個檔案位置加上的,os.path.dirname(__file__)抓目前的檔案位置資料夾

所有用到位置的都要加上這行,用到位置的有,標楷體字體檔案位置,輸出檔案位置。
在__init__() 有傳dataDictList,就是傳抓到的資料,這邊就直接顯示就好,不做任何資料處理,還有傳fileName,可以指定位置儲存。
在export(),就是產生pdf 把資料list 跑迴圈去呼叫之前寫好的表格和圖表。

styles = getSampleStyleSheet()

styleNormalCustom = ParagraphStyle(
    'styleNormalCustom',
    fontName='kaiu',
    parent=styles["Normal"],
    alignment=TA_CENTER,
)
...
class PdfWeather:
    def __init__(self, dataDictList, fileName=None):
        # 抓目前的檔案位置資料夾
        filePath = os.path.dirname(__file__)
        fontPath = os.path.join(filePath, "font/kaiu.ttf")
        pdfmetrics.registerFont(TTFont('kaiu', fontPath))
        if fileName:
            self._fileName = fileName
        else:
            self._fileName = os.path.join(filePath, "example.pdf")
        self._dataDictList = dataDictList

    def export(self):
        todayDate = datetime.datetime.today().date()
        tomorrow = todayDate + datetime.timedelta(days=1)
        tomorrowText = tomorrow.strftime('%Y-%m-%d')

        pdfTemplate = SimpleDocTemplate(self._fileName)
        story = []
        for dataDict in self._dataDictList:
            story.append(Paragraph("未來鄉鎮天氣預報", style=styleNormalCustomHeader))
            story.append(Spacer(1, 0.15 * inch))
            story.append(Paragraph(dataDict["locationName"], style=styleNormalCustomHeader2))
            story.append(Spacer(1, 0.15 * inch))
            table = self.getPDFTableDistrict(dataDict, tomorrowText)
            story.append(table)
            story.append(Spacer(1, 0.15 * inch))
            story.append(Paragraph("未來鄉鎮溫度折線圖", style=styleNormalCustomHeader2))
            chart = self.getPDFChartDistrict(dataDict, tomorrowText)
            story.append(chart)
            story.append(Spacer(1, 0.2 * inch))
            story.append(Paragraph("未來鄉鎮降雨機率折線圖", style=styleNormalCustomHeader2))
            chart = self.getPDFChartDistrict2(dataDict, tomorrowText)
            story.append(chart)
            story.append(PageBreak())

        pdfTemplate.build(story)

    # 處理溫度、體感溫度圖表
    def getPDFChartDistrict(self, dataDict, showDateText):
        ... 
        return drawing
        
    # 處理降雨機率的圖表
    def getPDFChartDistrict2(self, dataDict, showDateText):
        ...
        return drawing
        
    # 處理表格顯示 所有欄位的表格
    def getPDFTableDistrict(self, dataDict, showDateText):
        ...
        return table

會這樣做,是想把權責分清楚,在之後維護上或找bug會比較好找。

當然也可以不用這樣做,這是我寫程式的習慣而已。


如果有任何寫得不好的地方,請跟我說,謝謝。


上一篇
[Day 18] Django(2)
下一篇
[Day 20] api
系列文
用 Python 玩 PDF,結合Django 變成一個網頁系統30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言