uw-course 1.0.1__py3-none-any.whl → 2.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,4 @@
1
1
  import re
2
- from ..Utiles.colorMessage import *
3
2
 
4
3
 
5
4
  class Course():
@@ -20,7 +19,7 @@ class Course():
20
19
  self.courseIndex = course['ClassIndex']
21
20
  self.courseTitle = course['classTitle']
22
21
  self.courseSeat = course['availableSeat']
23
- if re.search("\d:\d*", self.time):
22
+ if re.search(r"\d:\d*", self.time):
24
23
  self.startTime = self.time[0:5]
25
24
  self.endTime = self.time[6:11]
26
25
  StartTimeFlag = False
@@ -37,7 +36,7 @@ class Course():
37
36
  self.weekDay = "H" if self.time[11:15] == "Th" else self.time[11:15]
38
37
  self.writeSchedule()
39
38
  else:
40
- print(WARNING + "Z_Z For course %s in class %s, Time Data Error Z_Z" % (self.courseIndex, self.classNum)+ENDC)
39
+ print("Z_Z For course %s in class %s, Time Data Error Z_Z" % (self.courseIndex, self.classNum))
41
40
 
42
41
  def writeSchedule(self):
43
42
  self.fileOut.write(
@@ -1,38 +1,49 @@
1
- from .SearchInfo import Course
2
- from ..Utiles.randomColor import randomColor, randomGray
3
- from ..setting import Setting
4
- from ..Utiles.colorMessage import *
1
+ from uw_course.ClassSchedule.SearchInfo import Course
2
+ from uw_course.Utiles.randomColor import randomColor, randomGray
3
+ from uw_course.setting import Setting
5
4
 
6
5
  from os import remove
7
6
 
8
7
  setting = Setting()
9
8
 
10
9
 
11
- def SearchCourse(dbClassUW, courseIndex):
10
+ def get_course_detail(dbClassUW, courseIndex):
12
11
  CourseDescribe = dbClassUW.CourseDescribe
12
+ if not courseIndex or " " not in courseIndex:
13
+ return None
13
14
  faculty = courseIndex.split(" ")[0]
14
15
  courseNum = courseIndex.split(" ")[1]
15
16
  FacultyList = CourseDescribe.find({"faculty": faculty})
16
17
  for course in FacultyList:
17
- if course["courseIndex"] == courseNum:
18
- print("\n" + "$" * 50 + "\n\n")
19
- print("Description: ", end="")
20
- print(course["courseDescription"])
21
- print("\ncourseCredit: ", end="")
22
- print(course["courseCredit"])
23
- print("\n\n" + "$" * 50)
24
- break
18
+ if course.get("courseIndex") == courseNum:
19
+ return course
20
+ return None
21
+
22
+
23
+ def SearchCourse(dbClassUW, courseIndex):
24
+ course = get_course_detail(dbClassUW, courseIndex)
25
+ if not course:
26
+ print("@_@ Course %s Not Found @_@" % (courseIndex))
27
+ return
28
+ print("\n" + "$" * 50 + "\n\n")
29
+ print("Description: ", end="")
30
+ print(course.get("courseDescription"))
31
+ print("\ncourseCredit: ", end="")
32
+ print(course.get("courseCredit"))
33
+ print("\n\n" + "$" * 50)
25
34
  print("\n\n")
26
35
 
27
36
 
28
- def SearchAvalibleInTerm(dbClassUW, courseIndex, classNum=None):
37
+ def SearchAvalibleInTerm(dbClassUW, courseIndex, classNum=None, quiet=False):
29
38
  ClassSchedule = dbClassUW.ClassSchedule
30
39
  courseSelect = ClassSchedule.find({"ClassIndex": courseIndex})
31
40
  if courseSelect == None:
32
- print(FAIL + "@_@ Course %s Not Found in %s @_@" % (courseIndex, dbClassUW.ClassCollectionName) + ENDC)
41
+ if not quiet:
42
+ print("@_@ Course %s Not Found in %s @_@" % (courseIndex, dbClassUW.ClassCollectionName))
33
43
  return None
34
44
  else:
35
- print(OKGREEN + "!! Found Course %s in %s !!" % (courseIndex, dbClassUW.ClassCollectionName) + ENDC)
45
+ if not quiet:
46
+ print("!! Found Course %s in %s !!" % (courseIndex, dbClassUW.ClassCollectionName))
36
47
  if classNum != None:
37
48
  return [courseIndex, classNum]
38
49
  return [courseIndex]
@@ -41,7 +52,7 @@ def SearchAvalibleInTerm(dbClassUW, courseIndex, classNum=None):
41
52
  def makeSchedule(dbClassUW, courseWishList: list, gray: bool=False):
42
53
  classSchedule = dbClassUW.ClassSchedule
43
54
  if gray:
44
- print(OKGREEN + "!! Make Schedule with Gray Color !!" + ENDC)
55
+ print("!! Make Schedule with Gray Color !!")
45
56
  try:
46
57
  remove(setting.outDir)
47
58
  except:
uw_course/DB/dbClass.py CHANGED
@@ -23,3 +23,23 @@ class dbClass:
23
23
  self.ClassCollectionName = collectionName
24
24
  self.ClassDATABASE.selectCollection(self.ClassCollectionName)
25
25
  self.ClassSchedule = self.ClassDATABASE.mongo_collection
26
+
27
+ def listClassCollections(self):
28
+ collections = self.ClassDATABASE.listCollections()
29
+ term_order = {"Winter": 0, "Spring": 1, "Fall": 2}
30
+
31
+ def sort_key(name):
32
+ if not name.startswith("Class"):
33
+ return (9999, 99, name)
34
+ tail = name[len("Class") :]
35
+ year = tail[:4]
36
+ term = tail[4:]
37
+ try:
38
+ year_value = int(year)
39
+ except ValueError:
40
+ year_value = 9999
41
+ term_value = term_order.get(term, 99)
42
+ return (year_value, term_value, name)
43
+
44
+ class_names = [name for name in collections if name.startswith("Class")]
45
+ return sorted(class_names, key=sort_key, reverse=True)
@@ -16,5 +16,10 @@ class connectDB:
16
16
  def selectCollection(self, collection_name):
17
17
  self.mongo_collection = self.mongo_db[collection_name]
18
18
 
19
+ def listCollections(self):
20
+ if self.mongo_db is None:
21
+ return []
22
+ return self.mongo_db.list_collection_names()
23
+
19
24
  def closeDB(self):
20
25
  self.client.close()
@@ -1,16 +1,28 @@
1
- from random import randint
1
+ from random import choice
2
+
3
+ _PALETTE = [
4
+ "#1F77B4", # blue
5
+ "#FF7F0E", # orange
6
+ "#2CA02C", # green
7
+ "#D62728", # red
8
+ "#9467BD", # purple
9
+ "#8C564B", # brown
10
+ "#E377C2", # pink
11
+ "#7F7F7F", # gray
12
+ "#BCBD22", # olive
13
+ "#17BECF", # cyan
14
+ "#4E79A7", # blue
15
+ "#F28E2B", # orange
16
+ "#59A14F", # green
17
+ "#E15759", # red
18
+ "#76B7B2", # teal
19
+ "#EDC948", # yellow
20
+ ]
2
21
 
3
22
 
4
23
  def randomColor():
5
- colorArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
6
- color = ""
7
- for i in range(6):
8
- color += colorArr[randint(0, 14)]
9
- return "#" + color
24
+ return choice(_PALETTE)
10
25
 
11
26
 
12
27
  def randomGray():
13
- colorArr = ['1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F']
14
- color = colorArr[randint(2, 12)]
15
- color += colorArr[randint(0, 14)]
16
- return "#" + color + color + color
28
+ return "#9E9E9E"
uw_course/main.py CHANGED
@@ -1,85 +1,16 @@
1
- from .DB.dbClass import dbClass
2
- from .ClassSchedule.runner import SearchCourse, makeSchedule, SearchAvalibleInTerm
3
- from .Utiles.colorMessage import *
4
- from .setting import Setting
5
-
6
- from os import system
7
- import argparse
1
+ import os
8
2
  import sys
9
3
 
10
- dbClassUW = dbClass()
11
- setting = Setting()
12
- courseWishList = []
13
-
14
- url = "https://github.com/zangjiucheng/CourseExplorer/blob/Release/schema.txt"
15
-
16
- def addCourse(course):
17
- if course != None:
18
- courseWishList.append(course)
19
-
4
+ if __package__ in (None, ""):
5
+ sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
6
+ __package__ = "uw_course"
20
7
 
21
- def checkDetail(course):
22
- SearchCourse(dbClassUW, course)
23
- exit(0)
24
-
25
- def parse_arguments():
26
- """Parse command-line arguments and validate them."""
27
- parser = argparse.ArgumentParser(
28
- description="Course Helper: A tool to manage course details and collections.",
29
- epilog=f"""Hint: Use --course to specify a course
30
- or --file to specify a file with courses using schema: {url}
31
- or --export to export the schedule to pdf (.out only).""",
32
- )
33
- parser.add_argument(
34
- "-c", "--course", type=str, help="Specify the course to check details for."
35
- )
36
- parser.add_argument(
37
- "-f", "--file", type=str, help="Specify a file with collection of courses."
38
- )
39
- parser.add_argument(
40
- "-e", "--export", action=str, help="Export the schedule to pdf (.out only)."
41
- )
42
- parser.add_argument(
43
- "-g", "--gray", action="store_true", help="Enable gray color mode for output."
44
- )
8
+ from uw_course.ui.app import run_app
45
9
 
46
- args = parser.parse_args()
47
-
48
- # Ensure at least one argument is provided
49
- if len(sys.argv) == 1:
50
- parser.print_help()
51
- sys.exit(1)
52
-
53
- return args
54
10
 
55
11
  def main():
56
- args = parse_arguments()
57
- gray = args.gray
58
- if args.course:
59
- checkDetail(args.course)
60
- elif args.export:
61
- system("pdfschedule " + args.export)
62
- print(OKGREEN + "\n\n ---------------- Done!!! ---------------- \n\n" + ENDC)
63
- elif args.file:
64
- with open(args.file, "r") as f:
65
- collection = f.readline().strip().split("#")[0].strip()
66
- dbClassUW.switchCollection(collection)
67
- next(f) # Skip the first line
68
- for line in f:
69
- try:
70
- if line.startswith("#"):
71
- continue
72
- info = line.strip().split(",")
73
- course = info[0].strip()
74
- if len(info) > 1:
75
- addCourse(SearchAvalibleInTerm(dbClassUW, course, int(info[1].strip())))
76
- else:
77
- addCourse(SearchAvalibleInTerm(dbClassUW, course))
78
- except Exception as e:
79
- print(FAIL + f"Error: {e}" + ENDC)
80
- makeSchedule(dbClassUW, courseWishList=courseWishList, gray=gray)
81
- system("pdfschedule " + setting.outDir)
82
- print(OKGREEN + "\n\n ---------------- Done!!! ---------------- \n\n" + ENDC)
12
+ run_app()
13
+
83
14
 
84
15
  if __name__ == "__main__":
85
- main()
16
+ main()
@@ -0,0 +1,385 @@
1
+ #!/usr/bin/python3
2
+ """
3
+ Weekly schedule typesetter
4
+
5
+ Run ``pdfschedule --help`` or visit <https://github.com/jwodder/schedule> for
6
+ more information.
7
+ """
8
+
9
+ __version__ = "0.4.1.post1"
10
+ __author__ = "John Thorvald Wodder II; modified by Jiucheng Zang"
11
+ __author_email__ = "pdfschedule@varonathe.org, git.jiucheng@gmail.com"
12
+ __license__ = "MIT"
13
+ __url__ = "https://github.com/jwodder/schedule"
14
+
15
+ from collections.abc import Mapping
16
+ from datetime import time
17
+ from math import ceil, floor
18
+ from pathlib import Path
19
+ import re
20
+ from textwrap import wrap
21
+ import attr
22
+ from reportlab.lib import pagesizes
23
+ from reportlab.lib.units import inch
24
+ from reportlab.pdfbase import pdfmetrics
25
+ from reportlab.pdfbase.ttfonts import TTFont
26
+ from reportlab.pdfgen.canvas import Canvas
27
+ import yaml
28
+
29
+ EM = 0.6 ### TODO: Eliminate
30
+
31
+ WEEKDAYS_EN = ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday"]
32
+ FULL_WEEK_EN = ["Sunday"] + WEEKDAYS_EN + ["Saturday"]
33
+ FULL_WEEK_MON_EN = WEEKDAYS_EN + ["Saturday", "Sunday"]
34
+
35
+ DAY_REGEXES = [
36
+ ("Sunday", "Sun?"),
37
+ ("Monday", "M(on?)?"),
38
+ ("Tuesday", "T(ue?)?"),
39
+ ("Wednesday", "W(ed?)?"),
40
+ ("Thursday", "Thu?|H|R"),
41
+ ("Friday", "F(ri?)?"),
42
+ ("Saturday", "Sat?"),
43
+ ]
44
+
45
+ GREY = (0.8, 0.8, 0.8)
46
+
47
+ COLORS = [
48
+ GREY,
49
+ (1, 0, 0), # red
50
+ (0, 1, 0), # blue
51
+ (0, 0, 1), # green
52
+ (0, 1, 1), # cyan
53
+ (1, 1, 0), # yellow
54
+ (0.5, 0, 0.5), # purple
55
+ (1, 1, 1), # white
56
+ (1, 0.5, 0), # orange
57
+ (1, 0, 1), # magenta
58
+ ]
59
+
60
+
61
+ class Schedule:
62
+ def __init__(self, days, day_names=None):
63
+ self.events = []
64
+ self.days = list(days)
65
+ if day_names is None:
66
+ self._day_names = lambda d: d
67
+ elif isinstance(day_names, Mapping):
68
+ self._day_names = day_names.__getitem__
69
+ elif not callable(day_names):
70
+ raise TypeError("day_names must be a callable or dict")
71
+ else:
72
+ self._day_names = day_names
73
+
74
+ def add_event(self, event):
75
+ self.events.append(event)
76
+
77
+ def day_names(self):
78
+ return map(self._day_names, self.days)
79
+
80
+ def all_events(self):
81
+ return self.events
82
+
83
+ def events_on_day(self, day):
84
+ return [e for e in self.events if day in e.days]
85
+
86
+ @property
87
+ def number_of_days(self):
88
+ return len(self.days)
89
+
90
+ # The font and pagesize of the canvas must already have been set.
91
+ # x,y: upper-left corner of schedule to render (counting times along the
92
+ # edge; `render` should not draw anything outside the given box)
93
+ def render(
94
+ self,
95
+ canvas,
96
+ width,
97
+ height,
98
+ x,
99
+ y,
100
+ font_size,
101
+ show_times=True,
102
+ min_time=None,
103
+ max_time=None,
104
+ ):
105
+ if min_time is None:
106
+ min_time = max(
107
+ min(time2hours(ev.start_time) for ev in self.all_events()) - 0.5, 0
108
+ )
109
+ if max_time is None:
110
+ max_time = min(
111
+ max(time2hours(ev.end_time) for ev in self.all_events()) + 0.5, 24
112
+ )
113
+ # List of hours to label and draw a line across
114
+ hours = range(floor(min_time) + 1, ceil(max_time))
115
+ line_height = font_size * 1.2
116
+ # Font size of the day headers at the top of each column:
117
+ header_size = font_size * 1.2
118
+ # Height of the boxes in which the day headers will be drawn:
119
+ day_height = header_size * 1.2
120
+ # Font size of the time labels at the left of each hour:
121
+ time_size = font_size / 1.2
122
+ # Boundaries of where this method is allowed to draw stuff:
123
+ area = Box(x, y, width, height)
124
+
125
+ canvas.setFontSize(time_size)
126
+ # Gap between the right edge of the time labels and the left edge of
127
+ # the schedule box. I don't remember how I came up with this formula.
128
+ time_gap = 0.2 * canvas.stringWidth(":00")
129
+ if show_times:
130
+ time_width = time_gap + max(canvas.stringWidth(f"{i}:00") for i in hours)
131
+ else:
132
+ time_width = 0
133
+
134
+ sched = Box(
135
+ x + time_width,
136
+ y - day_height,
137
+ width - time_width,
138
+ height - day_height,
139
+ )
140
+ hour_height = sched.height / (max_time - min_time)
141
+ day_width = sched.width / self.number_of_days
142
+ line_width = floor(day_width / (font_size * EM))
143
+
144
+ # Border around schedule and day headers:
145
+ canvas.rect(sched.ulx, sched.lry, sched.width, area.height)
146
+
147
+ # Day headers text:
148
+ canvas.setFontSize(header_size)
149
+ for i, day in enumerate(self.day_names()):
150
+ canvas.drawCentredString(
151
+ sched.ulx + day_width * (i + 0.5),
152
+ area.uly - line_height,
153
+ day,
154
+ )
155
+
156
+ # Underline beneath day headers:
157
+ canvas.line(sched.ulx, sched.uly, sched.lrx, sched.uly)
158
+
159
+ # Lines across each hour:
160
+ canvas.setDash([2], 0)
161
+ for i in hours:
162
+ y = sched.uly - (i - min_time) * hour_height
163
+ canvas.line(sched.ulx, y, sched.lrx, y)
164
+
165
+ # Lines between each day:
166
+ canvas.setDash([], 0)
167
+ for i in range(1, self.number_of_days):
168
+ x = sched.ulx + i * day_width
169
+ canvas.line(x, area.uly, x, area.lry)
170
+
171
+ if show_times:
172
+ canvas.setFontSize(time_size)
173
+ for i in hours:
174
+ canvas.drawRightString(
175
+ sched.ulx - time_gap,
176
+ sched.uly - (i - min_time) * hour_height - time_size / 2,
177
+ f"{i}:00",
178
+ )
179
+
180
+ # Events:
181
+ canvas.setFontSize(font_size)
182
+ for i, day in enumerate(self.days):
183
+ dx = sched.ulx + day_width * i
184
+ for ev in self.events_on_day(day):
185
+ ebox = Box(
186
+ dx,
187
+ sched.uly - (time2hours(ev.start_time) - min_time) * hour_height,
188
+ day_width,
189
+ ev.length * hour_height,
190
+ )
191
+ # Event box:
192
+ canvas.setStrokeColorRGB(0, 0, 0)
193
+ canvas.setFillColorRGB(*ev.color)
194
+ canvas.rect(*ebox.rect(), stroke=1, fill=1)
195
+ canvas.setFillColorRGB(0, 0, 0)
196
+
197
+ if ev.color[1] <= 0.33333:
198
+ # Background color is too dark; print text in white
199
+ canvas.setFillColorRGB(1, 1, 1)
200
+
201
+ # Event text:
202
+ ### TODO: Use PLATYPUS or whatever for this part:
203
+ text = sum((wrap(t, line_width) for t in ev.text), [])
204
+ tmp_size = None
205
+ if len(text) * line_height > ebox.height:
206
+ tmp_size = ebox.height / len(text) / 1.2
207
+ canvas.setFontSize(tmp_size)
208
+ line_height = tmp_size * 1.2
209
+ y = (
210
+ ebox.lry
211
+ + ebox.height / 2
212
+ + len(text) * line_height / 2
213
+ + (tmp_size or font_size) / 3
214
+ )
215
+ for t in text:
216
+ y -= line_height
217
+ canvas.drawCentredString(ebox.ulx + day_width / 2, y, t)
218
+ if tmp_size is not None:
219
+ canvas.setFontSize(font_size)
220
+ line_height = font_size * 1.2
221
+
222
+
223
+ @attr.define
224
+ class Event:
225
+ start_time: time = attr.field(validator=attr.validators.instance_of(time))
226
+ end_time: time = attr.field(validator=attr.validators.instance_of(time))
227
+ text: list[str] = attr.field()
228
+ color: tuple[float, float, float] = attr.field()
229
+ days: list[str] = attr.field() # List of days
230
+
231
+ def __attrs_post_init__(self):
232
+ if self.start_time >= self.end_time:
233
+ raise ValueError("Event must start before it ends")
234
+
235
+ @property
236
+ def length(self):
237
+ """The length of the event in hours"""
238
+ return timediff(self.start_time, self.end_time)
239
+
240
+
241
+ @attr.define
242
+ class Box:
243
+ ulx: float = attr.field()
244
+ uly: float = attr.field()
245
+ width: float = attr.field()
246
+ height: float = attr.field()
247
+
248
+ @property
249
+ def lrx(self):
250
+ return self.ulx + self.width
251
+
252
+ @property
253
+ def lry(self):
254
+ return self.uly - self.height
255
+
256
+ def rect(self):
257
+ return (self.ulx, self.lry, self.width, self.height)
258
+
259
+
260
+ def parse_time(s):
261
+ m = re.fullmatch(r"([0-9]{1,2})(?:[:.]?([0-9]{2}))?", s.strip())
262
+ if m:
263
+ return time(int(m[1]), int(m[2] or 0))
264
+ else:
265
+ raise ValueError(s)
266
+
267
+
268
+ def generate_pdf(
269
+ infile_path,
270
+ outfile_path=None,
271
+ color=False,
272
+ font="Helvetica",
273
+ font_size=10,
274
+ portrait=False,
275
+ scale=None,
276
+ no_times=False,
277
+ no_weekends=False,
278
+ start_monday=False,
279
+ start_time=None,
280
+ end_time=None,
281
+ ):
282
+ if font in available_fonts():
283
+ font_name = font
284
+ else:
285
+ font_name = "CustomFont"
286
+ pdfmetrics.registerFont(TTFont(font_name, font))
287
+ if portrait:
288
+ page_width, page_height = pagesizes.portrait(pagesizes.letter)
289
+ else:
290
+ page_width, page_height = pagesizes.landscape(pagesizes.letter)
291
+ colors = COLORS if color else [GREY]
292
+ if no_weekends:
293
+ week = WEEKDAYS_EN
294
+ elif start_monday:
295
+ week = FULL_WEEK_MON_EN
296
+ else:
297
+ week = FULL_WEEK_EN
298
+ sched = Schedule(week)
299
+ with open(infile_path, "r", encoding="utf-8") as infile:
300
+ for ev in read_events(infile, colors=colors):
301
+ sched.add_event(ev)
302
+ if outfile_path is None:
303
+ outfile_path = str(Path(infile_path).with_suffix(".pdf"))
304
+ with open(outfile_path, "wb") as outfile:
305
+ c = Canvas(outfile, (page_width, page_height))
306
+ c.setFont(font_name, font_size)
307
+ if scale is not None:
308
+ factor = 1 / scale
309
+ c.translate(
310
+ (1 - factor) * page_width / 2,
311
+ (1 - factor) * page_height / 2,
312
+ )
313
+ c.scale(factor, factor)
314
+ sched.render(
315
+ c,
316
+ x=inch,
317
+ y=page_height - inch,
318
+ width=page_width - 2 * inch,
319
+ height=page_height - 2 * inch,
320
+ font_size=font_size,
321
+ show_times=not no_times,
322
+ min_time=time2hours(start_time) if start_time is not None else None,
323
+ max_time=time2hours(end_time) if end_time is not None else None,
324
+ )
325
+ c.showPage()
326
+ c.save()
327
+
328
+
329
+ def read_events(infile, colors):
330
+ indata = yaml.safe_load(infile)
331
+ if not isinstance(indata, list):
332
+ raise ValueError("Input must be a YAML list")
333
+ for i, entry in enumerate(indata):
334
+ text = entry.get("name", "").splitlines()
335
+ try:
336
+ days = entry["days"]
337
+ timestr = entry["time"]
338
+ except KeyError as e:
339
+ raise ValueError(f"{str(e)!r} field missing from event #{i + 1}")
340
+ start_str, _, end_str = timestr.partition("-")
341
+ try:
342
+ start = parse_time(start_str)
343
+ end = parse_time(end_str)
344
+ except ValueError:
345
+ raise ValueError(f"Invalid time: {timestr!r}")
346
+ if "color" in entry:
347
+ m = re.fullmatch(
348
+ r"\s*#?\s*([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})\s*",
349
+ entry["color"],
350
+ )
351
+ if not m:
352
+ raise ValueError("Invalid color: " + repr(entry["color"]))
353
+ color = (
354
+ int(m.group(1), 16) / 255,
355
+ int(m.group(2), 16) / 255,
356
+ int(m.group(3), 16) / 255,
357
+ )
358
+ else:
359
+ color = colors[i % len(colors)]
360
+ yield Event(
361
+ start_time=start,
362
+ end_time=end,
363
+ text=text,
364
+ color=color,
365
+ days=[d for d, rgx in DAY_REGEXES if re.search(rgx, days)],
366
+ )
367
+
368
+
369
+ def time2hours(t):
370
+ return t.hour + (t.minute + (t.second + t.microsecond / 1000000) / 60) / 60
371
+
372
+
373
+ def timediff(t1, t2):
374
+ # Returns the difference between two `datetime.time` objects as a number of
375
+ # hours
376
+ return time2hours(t2) - time2hours(t1)
377
+
378
+
379
+ def available_fonts():
380
+ return Canvas("").getAvailableFonts()
381
+ # return pdfmetrics.standardFonts
382
+
383
+
384
+ if __name__ == "__main__":
385
+ raise SystemExit("This module is intended to be imported and used as a library.")
uw_course/setting.py CHANGED
@@ -1,5 +1,10 @@
1
+ from os import makedirs
2
+ from os.path import join
3
+
4
+
1
5
  class Setting:
2
6
  def __init__(self):
3
- self.dataName = "./"
7
+ self.dataName = "./uw-course-files"
4
8
  self.configFileName = "schedule.out"
5
- self.outDir = self.dataName + "/" + self.configFileName
9
+ makedirs(self.dataName, exist_ok=True)
10
+ self.outDir = join(self.dataName, self.configFileName)
@@ -0,0 +1 @@
1
+ """UI package for uw_course."""