├── lastUpdate.txt
├── img
├── ogp.png
├── graph.png
└── icon.png
├── style.css
├── README.md
├── .github
└── workflows
│ └── update.yml
├── update.py
└── index.html
/lastUpdate.txt:
--------------------------------------------------------------------------------
1 | 2023/5/8 16:45
--------------------------------------------------------------------------------
/img/ogp.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/morioprog/covid19-rating-graph/HEAD/img/ogp.png
--------------------------------------------------------------------------------
/img/graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/morioprog/covid19-rating-graph/HEAD/img/graph.png
--------------------------------------------------------------------------------
/img/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/morioprog/covid19-rating-graph/HEAD/img/icon.png
--------------------------------------------------------------------------------
/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | display: flex;
3 | justify-content: center;
4 | align-items: center;
5 | }
6 |
7 | ul {
8 | list-style: disc;
9 | }
10 |
11 | .ul-center {
12 | display: flex;
13 | align-items: center;
14 | flex-direction: column;
15 | }
16 |
17 | .center {
18 | text-align: center;
19 | }
20 |
21 | .emphasize {
22 | color: red;
23 | font-weight: bold;
24 | }
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 | [](https://morioprog.github.io/covid19-rating-graph/)
9 | [](https://twitter.com/covid19_rating)
10 | [](https://github.com/morioprog/covid19-rating-graph/actions/workflows/update.yml)
11 |
12 | [](https://morioprog.github.io/covid19-rating-graph/)
13 |
14 |
--------------------------------------------------------------------------------
/.github/workflows/update.yml:
--------------------------------------------------------------------------------
1 | name: "Update HTML"
2 |
3 | on:
4 | push:
5 | branches:
6 | - main
7 | schedule:
8 | - cron: "*/20 * * * *"
9 |
10 | jobs:
11 | update:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v2
15 | - name: Setup Python
16 | uses: actions/setup-python@v2
17 | with:
18 | python-version: "3.x"
19 | - name: Install dependencies
20 | run: |
21 | python -m pip install --upgrade pip
22 | pip install requests selenium Pillow tweepy
23 | - name: Install Japanese font
24 | run: sudo apt-get install fonts-ipafont
25 | - name: Prepare Selenium
26 | uses: nanasess/setup-chromedriver@v1.0.5
27 | - name: Start XVFB
28 | run: Xvfb :99 &
29 | - name: Update
30 | run: |
31 | mkdir -p img
32 | if python update.py; then
33 | git config user.name github-actions
34 | git config user.email github-actions@github.com
35 | git add index.html img/ogp.png img/graph.png lastUpdate.txt
36 | git commit -m "Update index.html and images"
37 | git push
38 | fi
39 | env:
40 | DISPLAY: :99
41 | TWITTER_API_KEY: ${{ secrets.TWITTER_API_KEY }}
42 | TWITTER_API_SECRET_KEY: ${{ secrets.TWITTER_API_SECRET_KEY }}
43 | TWITTER_ACCESS_TOKEN: ${{ secrets.TWITTER_ACCESS_TOKEN }}
44 | TWITTER_ACCESS_TOKEN_SECRET: ${{ secrets.TWITTER_ACCESS_TOKEN_SECRET }}
45 |
--------------------------------------------------------------------------------
/update.py:
--------------------------------------------------------------------------------
1 | import os
2 | import sys
3 | import time
4 | import tweepy
5 | import datetime
6 | import requests
7 | from PIL import Image
8 | from selenium import webdriver
9 |
10 |
11 | def fetch_api():
12 | api = 'https://raw.githubusercontent.com/tokyo-metropolitan-gov/covid19/master/data/daily_positive_detail.json'
13 | req = requests.get(api)
14 | json = req.json()
15 | last_update = json['date']
16 | data = json['data']
17 | return last_update, data
18 |
19 |
20 | def update_lastupdate(last_update, file='lastUpdate.txt'):
21 | with open(file, mode='r') as f:
22 | if f.read() == last_update:
23 | return False
24 | with open(file, mode='w') as f:
25 | f.write(last_update)
26 | return True
27 |
28 |
29 | def rate_to_color(rate):
30 | if rate >= 2800:
31 | return '#FF0000'
32 | if rate >= 2400:
33 | return '#FF8000'
34 | if rate >= 2000:
35 | return '#C0C000'
36 | if rate >= 1600:
37 | return '#0000FF'
38 | if rate >= 1200:
39 | return '#00C0C0'
40 | if rate >= 800:
41 | return '#008000'
42 | if rate >= 400:
43 | return '#804000'
44 | if rate > 0:
45 | return '#808080'
46 | return '#000000'
47 |
48 |
49 | def rating_diff(old_rating, new_rating):
50 | return f"{'+' if old_rating < new_rating else ''}{new_rating - old_rating}"
51 |
52 |
53 | def convert_dict(dct):
54 | ret = []
55 | old_rating = 0
56 | for e in dct:
57 | ret.append({
58 | "ContestName": "東京都新規陽性者数の遷移",
59 | "OldRating": old_rating,
60 | "NewRating": e['count'],
61 | "EndTime": datetime.datetime.strptime(f"{e['diagnosed_date']}+0000", '%Y-%m-%d%z').timestamp(),
62 | "Place": -1,
63 | "StandingsUrl": "#",
64 | })
65 | old_rating = e['count']
66 | return ret
67 |
68 |
69 | def generate_ogp_html(dct, old_r, new_r, dt):
70 | with open('ogp.html', mode='w') as f:
71 | f.write(f'''\
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
{last_update[5:-6]} ({"月火水木金土日"[dt.weekday()]})
87 |
{old_r}
88 |
↓
89 |
{new_r}
90 |
({rating_diff(old_r, new_r)})
91 |
92 |
93 |
94 |
95 |
96 |
99 |
100 |
101 | ''')
102 |
103 |
104 | def save_ogp_image():
105 | driver = webdriver.Chrome()
106 | driver.get(
107 | 'file:///home/runner/work/covid19-rating-graph/covid19-rating-graph/ogp.html')
108 | time.sleep(20) # ???
109 | driver.save_screenshot("img/ogp_bef.png")
110 | driver.quit()
111 |
112 | im = Image.open('img/ogp_bef.png')
113 | im.crop((0, 0, 838, 440)).save('img/ogp.png', quality=95)
114 |
115 | im = Image.open('img/ogp_bef.png')
116 | im.crop((0, 0, 640, 440)).save('img/graph.png', quality=95)
117 |
118 |
119 | def tweet_graph(tweet, img):
120 | AT = os.environ['TWITTER_ACCESS_TOKEN']
121 | AS = os.environ['TWITTER_ACCESS_TOKEN_SECRET']
122 | CK = os.environ['TWITTER_API_KEY']
123 | CS = os.environ['TWITTER_API_SECRET_KEY']
124 | auth = tweepy.OAuthHandler(CK, CS)
125 | auth.set_access_token(AT, AS)
126 | api = tweepy.API(auth)
127 | media_ids = [api.media_upload(img).media_id]
128 | api.update_status(status=tweet, media_ids=media_ids)
129 |
130 |
131 | def generate_index_html(dct, tweet):
132 | with open('index.html', mode='w') as f:
133 | f.write(f'''\
134 |
135 |
136 | COVID-19 Rating Graph
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
完全に非公式です。
156 |
157 |
158 |
〜
159 |
160 |
161 |
162 |
163 |
164 |
170 |
171 |
177 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
216 |
217 |
218 | ''')
219 |
220 |
221 | if __name__ == '__main__':
222 | last_update, data = fetch_api()
223 | # No update
224 | if not update_lastupdate(last_update):
225 | sys.exit(1)
226 | old_r = data[-2]['count']
227 | new_r = data[-1]['count']
228 | dct = convert_dict(data)
229 | y, m, d = map(int, last_update[:-6].split('/'))
230 | last_update_dt = datetime.datetime(year=y, month=m, day=d)
231 | generate_ogp_html(dct, old_r, new_r, last_update_dt)
232 | save_ogp_image()
233 | tweet = f'''\
234 | 東京都新規陽性者数({last_update[:-6]}更新)
235 | レーティング:{old_r}→{new_r} ({rating_diff(old_r, new_r)}) {':|' if old_r == new_r else ':(' if old_r < new_r else ':)'}
236 | {'Highestを更新してしまいました...' if max(e['count'] for e in data[:-1]) < data[-1]['count'] else ''}
237 | #COVID19RatingGraph
238 | '''
239 | tweet_graph(
240 | tweet + 'https://morioprog.github.io/covid19-rating-graph/', 'img/ogp.png')
241 | generate_index_html(dct, tweet)
242 | sys.exit(0)
243 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | COVID-19 Rating Graph
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
完全に非公式です。
23 |
24 |
25 |
〜
26 |
27 |
28 |
29 |
30 |
31 |
37 |
38 |
48 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
87 |
88 |
89 |
--------------------------------------------------------------------------------