update.py 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. #!/usr/bin/env python3
  2. """Script for updating data. """
  3. import argparse
  4. import json
  5. import os
  6. import re
  7. import subprocess
  8. from datetime import datetime, timedelta, tzinfo
  9. from tempfile import mkstemp
  10. from typing import Iterator
  11. from zipfile import ZipFile
  12. from tqdm import tqdm
  13. from fetch import CustomJSONEncoder, fetch_holiday
  14. from generate_ics import generate_ics
  15. class ChinaTimezone(tzinfo):
  16. """Timezone of china."""
  17. def tzname(self, dt):
  18. return "UTC+8"
  19. def utcoffset(self, dt):
  20. return timedelta(hours=8)
  21. def dst(self, dt):
  22. return timedelta()
  23. __dirname__ = os.path.abspath(os.path.dirname(__file__))
  24. def _file_path(*other):
  25. return os.path.join(__dirname__, *other)
  26. def update_data(year: int) -> Iterator[str]:
  27. """Update and store data for a year."""
  28. json_filename = _file_path(f"{year}.json")
  29. ics_filename = _file_path(f"{year}.ics")
  30. with open(json_filename, "w", encoding="utf-8", newline="\n") as f:
  31. data = fetch_holiday(year)
  32. json.dump(
  33. dict(
  34. (
  35. (
  36. "$schema",
  37. "https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/schema.json",
  38. ),
  39. (
  40. "$id",
  41. f"https://raw.githubusercontent.com/NateScarlet/holiday-cn/master/{year}.json",
  42. ),
  43. *data.items(),
  44. )
  45. ),
  46. f,
  47. indent=4,
  48. ensure_ascii=False,
  49. cls=CustomJSONEncoder,
  50. )
  51. yield json_filename
  52. generate_ics(data["days"], ics_filename)
  53. yield ics_filename
  54. def update_main_ics(fr_year, to_year):
  55. all_days = []
  56. for year in range(fr_year, to_year + 1):
  57. filename = _file_path(f"{year}.json")
  58. if not os.path.isfile(filename):
  59. continue
  60. with open(filename, "r", encoding="utf8") as inf:
  61. data = json.loads(inf.read())
  62. all_days.extend(data.get("days"))
  63. filename = _file_path("holiday-cn.ics")
  64. generate_ics(
  65. all_days,
  66. filename,
  67. )
  68. return filename
  69. def main():
  70. parser = argparse.ArgumentParser()
  71. parser.add_argument(
  72. "--all",
  73. action="store_true",
  74. help="Update all years since 2007, default is this year and next year",
  75. )
  76. parser.add_argument(
  77. "--release",
  78. action="store_true",
  79. help="create new release if repository data is not up to date",
  80. )
  81. args = parser.parse_args()
  82. now = datetime.now(ChinaTimezone())
  83. is_release = args.release
  84. filenames = []
  85. progress = tqdm(range(2007 if args.all else now.year, now.year + 2))
  86. for i in progress:
  87. progress.set_description(f"Updating {i} data")
  88. filenames += list(update_data(i))
  89. progress.set_description("Updating holiday-cn.ics")
  90. filenames.append(update_main_ics(now.year - 4, now.year + 1))
  91. print("")
  92. subprocess.run(["hub", "add", *filenames], check=True)
  93. diff = subprocess.run(
  94. ["hub", "diff", "--stat", "--cached", "*.json", "*.ics"],
  95. check=True,
  96. stdout=subprocess.PIPE,
  97. encoding="utf-8",
  98. ).stdout
  99. if not diff:
  100. print("Already up to date.")
  101. return
  102. if not is_release:
  103. print("Updated repository data, skip release since not specified `--release`")
  104. return
  105. subprocess.run(
  106. [
  107. "hub",
  108. "commit",
  109. "-m",
  110. "chore(release): update holiday data",
  111. "-m",
  112. "[skip ci]",
  113. ],
  114. check=True,
  115. )
  116. subprocess.run(["hub", "push"], check=True)
  117. tag = now.strftime("%Y.%m.%d")
  118. temp_note_fd, temp_note_name = mkstemp()
  119. with open(temp_note_fd, "w", encoding="utf-8") as f:
  120. f.write(tag + "\n\n```diff\n" + diff + "\n```\n")
  121. os.makedirs(_file_path("dist"), exist_ok=True)
  122. zip_path = _file_path("dist", f"holiday-cn-{tag}.zip")
  123. pack_data(zip_path)
  124. subprocess.run(
  125. [
  126. "hub",
  127. "release",
  128. "create",
  129. "-F",
  130. temp_note_name,
  131. "-a",
  132. f"{zip_path}#JSON数据",
  133. tag,
  134. ],
  135. check=True,
  136. )
  137. os.unlink(temp_note_name)
  138. def pack_data(file):
  139. """Pack data json in zip file."""
  140. zip_file = ZipFile(file, "w")
  141. for i in os.listdir(__dirname__):
  142. if not re.match(r"\d+\.json", i):
  143. continue
  144. zip_file.write(_file_path(i), i)
  145. if __name__ == "__main__":
  146. main()