This repository has been archived by the owner on Jul 2, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 199
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
413 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 }} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
from .src.core import DeliveryTracker | ||
|
||
__all__ = ["DeliveryTracker"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.