├── .gitattributes ├── .gitignore ├── README.md └── json_version.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | *.egg-info/ 24 | .installed.cfg 25 | *.egg 26 | MANIFEST 27 | 28 | # PyInstaller 29 | # Usually these files are written by a python script from a template 30 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 31 | *.manifest 32 | *.spec 33 | 34 | # Installer logs 35 | pip-log.txt 36 | pip-delete-this-directory.txt 37 | 38 | # Unit test / coverage reports 39 | htmlcov/ 40 | .tox/ 41 | .coverage 42 | .coverage.* 43 | .cache 44 | nosetests.xml 45 | coverage.xml 46 | *.cover 47 | .hypothesis/ 48 | .pytest_cache/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | db.sqlite3 58 | 59 | # Flask stuff: 60 | instance/ 61 | .webassets-cache 62 | 63 | # Scrapy stuff: 64 | .scrapy 65 | 66 | # Sphinx documentation 67 | docs/_build/ 68 | 69 | # PyBuilder 70 | target/ 71 | 72 | # Jupyter Notebook 73 | .ipynb_checkpoints 74 | 75 | # pyenv 76 | .python-version 77 | 78 | # celery beat schedule file 79 | celerybeat-schedule 80 | 81 | # SageMath parsed files 82 | *.sage.py 83 | 84 | # Environments 85 | .env 86 | .venv 87 | env/ 88 | venv/ 89 | ENV/ 90 | env.bak/ 91 | venv.bak/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | 106 | .idea 107 | *.ics 108 | *.txt 109 | *.html 110 | *.json -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # USTC-timetable-to-ics 2 | USTC timetable to calendar ics file 中国科学技术大学教务课程表转换日历ics文件 3 | 有了ics文件,可以导入到win10日历、Google日历、outlook日历等等…… 4 | 5 | # 使用方法 6 | 登录教务系统后(研究生、本科生),在浏览器开启F12调试窗口后打开```https://jw.ustc.edu.cn/for-std/course-table```,在调试窗口找到一个含有选课信息、个人信息、上课时间的返回结果(返回的是json),将返回内容文本复制到```json_version.py```文件目录下的```1.json```中, 7 | 然后**修改py文件**中学期第一周星期一前面一天(星期日)的日期,最后运行```json_version.py```,输出的ics就是日历文件。 8 | 9 | # 怎么导出到…… 10 | win10日历、outlook可以直接导入 11 | **Google日历网页版**直接上传ics导入**不行**,**要通过网址**添加,例如可以把ics文件托管在github pages上然后在Google日历中添加(这样无法添加到主日程表)。如果需要配合安卓勿扰功能,需要用手机版Google日历APP打开ics文件并导入。 12 | -------------------------------------------------------------------------------- /json_version.py: -------------------------------------------------------------------------------- 1 | import json, demjson 2 | from icalendar import Calendar, Event 3 | from datetime import datetime,timedelta 4 | from pytz import UTC # timezone 5 | from pytz import timezone 6 | 7 | cal = Calendar() 8 | cal.add('prodid', '-//ustc timetable//timetable//CN') 9 | cal.add('version', '2.0') 10 | cal.add('TZID','Asia/Shanghai') 11 | cal.add('X-WR-TIMEZONE','Asia/Shanghai') 12 | 13 | MODE = "CN" 14 | FIRST = datetime.strptime("2020-02-16 00:00:00","%Y-%m-%d %H:%M:%S").astimezone(timezone("Asia/Shanghai")) # 学期第一周的周日,即开学前一天 15 | 16 | s = str(open("1.json", 'r', encoding='UTF-8').readlines()) 17 | xx = demjson.decode(s) 18 | x = (demjson.decode(xx[0])) 19 | print(x['studentTableVm']['name'], x['studentTableVm']['code'], x['studentTableVm']['department'], "本学期学分", 20 | x['studentTableVm']['credits'], x['studentTableVm']['major']) 21 | ii = 0 22 | for c in x['studentTableVm']['activities']: 23 | summary = c['courseName'] 24 | location = " ".join([c['campus'] or c['customPlace'], c['room'] or '']) 25 | description = " ".join([c['campus'] or c['customPlace'], c['building'] or '', " ".join(c['teachers']), c['weeksStr'] + "周", c['lessonCode'], 26 | str(c['credits']) + '学分']) 27 | status = 0 # 0每周 1单周 2双周 28 | weeksStr = str(c['weeksStr']) 29 | if weeksStr.find("单") > -1: 30 | status = 1 31 | weeksStr=weeksStr.replace('单', '') 32 | elif weeksStr.find("双") > -1: 33 | status = 2 34 | weeksStr=weeksStr.replace('双', '') 35 | startWeek = int(weeksStr.split('-')[0]) 36 | endWeek = int(weeksStr.split('-')[1]) 37 | weekday = int(c['weekday']) 38 | sHour=int(c['startDate'].split(':')[0]) 39 | sMin = int(c['startDate'].split(':')[1]) 40 | eHour=int(c['endDate'].split(':')[0]) 41 | eMin = int(c['endDate'].split(':')[1]) 42 | event = Event() 43 | event.add('summary', summary) 44 | event.add('location', location) 45 | event.add('description', description) 46 | event.add('dtstart',FIRST+timedelta(days=weekday,weeks=startWeek-1,hours=sHour,minutes=sMin)) 47 | event.add('dtend', FIRST+timedelta(days=weekday,weeks=startWeek-1,hours=eHour,minutes=eMin)) 48 | event.add('dtstamp', datetime.utcnow()) 49 | interval = 2 if status else 1 50 | event.add('rrule', {'freq': 'weekly', 'interval': interval, 'count': (endWeek - startWeek) // interval + 1}) 51 | cal.add_component(event) 52 | f = open('example.ics', 'wb') 53 | f.write(cal.to_ical()) 54 | f.close() 55 | --------------------------------------------------------------------------------