Skip to content
This repository has been archived by the owner on Jul 2, 2024. It is now read-only.

Commit

Permalink
First
Browse files Browse the repository at this point in the history
  • Loading branch information
kst6294 committed Jun 27, 2024
1 parent 8f8c9cc commit 7f5f960
Show file tree
Hide file tree
Showing 7 changed files with 413 additions and 0 deletions.
79 changes: 79 additions & 0 deletions .github/workflows/workflow.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# .github/workflows/workflow.yaml

name: Publish

on:
push:
branches:
- main

jobs:
release:
runs-on: ubuntu-latest
permissions:
contents: write
id-token: write
steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: "3.10"

- name: Install Poetry and Packages
run: |
curl -sSL https://install.python-poetry.org | python3 -
poetry install
- name: Determine Version Change
id: version_check
run: |
VERSION="v$(poetry version -s)"
echo "Current version: $VERSION"
LATEST_RELEASE=$(curl -s -H "Authorization: token ${{ github.token }}" \
https://api.github.com/repos/${{ github.repository }}/releases/latest | jq -r '.tag_name')
echo "Latest release version: $LATEST_RELEASE"
if [ "$VERSION" != "$LATEST_RELEASE" ]; then
echo "Version has changed."
echo "version_changed=true" >> $GITHUB_OUTPUT
echo "new_version=$VERSION" >> $GITHUB_OUTPUT
else
echo "No version change detected."
echo "version_changed=false" >> $GITHUB_OUTPUT
fi
- name: Create Release
if: steps.version_check.outputs.version_changed == 'true'
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.version_check.outputs.new_version }}
generate_release_notes: True

- name: mint API token
if: steps.version_check.outputs.version_changed == 'true'
id: mint-token
run: |
# retrieve the ambient OIDC token
resp=$(curl -H "Authorization: bearer $ACTIONS_ID_TOKEN_REQUEST_TOKEN" \
"$ACTIONS_ID_TOKEN_REQUEST_URL&audience=pypi")
oidc_token=$(jq -r '.value' <<< "${resp}")
# exchange the OIDC token for an API token
resp=$(curl -X POST https://pypi.org/_/oidc/mint-token -d "{\"token\": \"${oidc_token}\"}")
api_token=$(jq -r '.token' <<< "${resp}")
# mask the newly minted API token, so that we don't accidentally leak it
echo "::add-mask::${api_token}"
# see the next step in the workflow for an example of using this step output
echo "api-token=${api_token}" >> "${GITHUB_OUTPUT}"
- name: Build and publish to PyPI
if: steps.version_check.outputs.version_changed == 'true'
run: |
poetry build
poetry publish -u __token__ -p ${{ steps.mint-token.outputs.api-token }}
3 changes: 3 additions & 0 deletions delivery_tracker/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
from .src.core import DeliveryTracker

__all__ = ["DeliveryTracker"]
17 changes: 17 additions & 0 deletions delivery_tracker/src/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import enum

from logistics.cjlogistics import CJLogistics


class CodeEnum(enum.Enum):
CJGLS = "01"

class DeliveryTracker:
def track(self, code, track_number):
try:
match CodeEnum(code):
case CodeEnum.CJGLS:
return CJLogistics().track(track_number=track_number)

except Exception:
return None
101 changes: 101 additions & 0 deletions delivery_tracker/src/logistics/cjlogistics.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import enum
import json
from dataclasses import dataclass, field
from http.cookies import SimpleCookie
from typing import List
from urllib.parse import urlencode

import requests
from bs4 import BeautifulSoup


@dataclass
class DeliveryStatus:
nsDlvNm: str
crgNm: str
crgSt: str
dTime: str
empImgNm: str
regBranId: str
regBranNm: str
scanNm: str


@dataclass
class TrackingResult:
invcNo: str
sendrNm: str
qty: str
itemNm: str
rcvrNm: str
rgmailNo: str
oriTrspbillnum: str
rtnTrspbillnum: str
nsDlvNm: str
delivery_status: List[DeliveryStatus] = field(default_factory=list)

@property
def latest_status(self) -> DeliveryStatus:
try:
return self.delivery_status[-1]
except Exception:
None


class DeliveryStatusEnum(enum.Enum):
PICKED_UP = "11" # 집화처리
IN_TRANSIT_TO_DISTRIBUTION = "21" # SM입고
IN_TRANSIT_TO_TERMINAL = "41" # 간선상차
IN_TRANSIT_TO_DESTINATION = "44" # 간선상차
ARRIVED_AT_DESTINATION = "42" # 간선하차
OUT_FOR_DELIVERY = "82" # 배송출발
DELIVERED = "91" # 배송완료


class CJLogistics:
MAIN_URL = "https://www.cjlogistics.com/ko/tool/parcel/tracking"
DETAIL_URL = "https://www.cjlogistics.com/ko/tool/parcel/tracking-detail"

def __get_main_page(self):
main_page_response = requests.get(self.MAIN_URL)
return main_page_response

def __get_headers(self, main_page_response):
cookie_headers = "; ".join(
[
f"{cookie.key}={cookie.value}"
for cookie in SimpleCookie(
main_page_response.headers.get("Set-Cookie", "")
).values()
]
)
return {"Cookie": cookie_headers}

def __get_csrf(self, main_page_response):
soup = BeautifulSoup(main_page_response.text, "html.parser")
csrf_token = soup.find("input", {"name": "_csrf"}).get("value")
return csrf_token

def track(self, track_number):
main_page_response = self.__get_main_page()
headers = self.__get_headers(main_page_response)
csrf_token = self.__get_csrf(main_page_response)
tracking_number = track_number
query_params = {
"paramInvcNo": tracking_number,
"_csrf": csrf_token,
}
tracking_detail_url = f"{self.DETAIL_URL}?{urlencode(query_params)}"
tracking_detail_response = requests.post(tracking_detail_url, headers=headers)

response_data = json.loads(tracking_detail_response.text)

tracking_result = TrackingResult(
**response_data["parcelResultMap"]["resultList"][0],
delivery_status=[
DeliveryStatus(**item)
for item in response_data["parcelDetailResultMap"]["resultList"]
],
)

return tracking_result
Loading

0 comments on commit 7f5f960

Please sign in to comment.