reykit 1.1.66__py3-none-any.whl → 1.1.67__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.
reykit/rschedule.py CHANGED
@@ -9,14 +9,15 @@
9
9
  """
10
10
 
11
11
 
12
- from typing import Any, Literal
12
+ from typing import Any, Literal, TypedDict, NotRequired
13
13
  from collections.abc import Callable
14
14
  from apscheduler.executors.pool import ThreadPoolExecutor
15
15
  from apscheduler.schedulers.background import BackgroundScheduler
16
16
  from apscheduler.schedulers.blocking import BlockingScheduler
17
17
  from apscheduler.job import Job
18
+ from reydb.rdb import Database
18
19
 
19
- from .rbase import Base
20
+ from .rbase import Base, throw
20
21
 
21
22
 
22
23
  __all__ = (
@@ -27,6 +28,7 @@ __all__ = (
27
28
  class Schedule(Base):
28
29
  """
29
30
  Schedule type.
31
+ Can create database used `self.build_db` method.
30
32
  """
31
33
 
32
34
 
@@ -35,7 +37,8 @@ class Schedule(Base):
35
37
  max_workers: int = 10,
36
38
  max_instances: int = 1,
37
39
  coalesce: bool = True,
38
- block: bool = False
40
+ block: bool = False,
41
+ database: Database | None = None
39
42
  ) -> None:
40
43
  """
41
44
  Build instance attributes.
@@ -46,6 +49,9 @@ class Schedule(Base):
46
49
  max_instances : Maximum number of synchronized executions of tasks with the same ID.
47
50
  coalesce : Whether to coalesce tasks with the same ID.
48
51
  block : Whether to block.
52
+ database : `Database` instance.
53
+ - `None`: Not use database.
54
+ - `Database`: Automatic record to database.
49
55
  """
50
56
 
51
57
  # Set parameter.
@@ -70,6 +76,14 @@ class Schedule(Base):
70
76
 
71
77
  # Set attribute.
72
78
  self.scheduler = scheduler
79
+ self.database = database
80
+
81
+ ## Database path name.
82
+ self.db_names = {
83
+ 'base': 'base',
84
+ 'base.schedule': 'schedule',
85
+ 'base.schedule_stats': 'schedule_stats'
86
+ }
73
87
 
74
88
 
75
89
  def start(self) -> None:
@@ -125,10 +139,9 @@ class Schedule(Base):
125
139
  def add_task(
126
140
  self,
127
141
  task: Callable,
128
- trigger: Literal['date', 'interval', 'cron'] = 'date',
129
- args: tuple | None = None,
130
- kwargs: dict | None = None,
131
- **trigger_kwargs: Any
142
+ plan: dict[str, Any],
143
+ *args: Any,
144
+ **kwargs: Any
132
145
  ) -> Job:
133
146
  """
134
147
  Add task.
@@ -136,23 +149,32 @@ class Schedule(Base):
136
149
  Parameters
137
150
  ----------
138
151
  task : Task function.
139
- trigger : Trigger type.
152
+ plan : Plan trigger keyword arguments.
140
153
  args : Task position arguments.
141
154
  kwargs : Task keyword arguments.
142
- trigger_kwargs : Trigger keyword arguments.
143
155
 
144
156
  Returns
145
157
  -------
146
158
  Task instance.
147
159
  """
148
160
 
161
+ # Handle parameter.
162
+ if plan is None:
163
+ plan = {}
164
+ trigger = plan.get('trigger')
165
+ trigger_args = {
166
+ key: value
167
+ for key, value in plan.items()
168
+ if key != 'trigger'
169
+ }
170
+
149
171
  # Add.
150
172
  job = self.scheduler.add_job(
151
173
  task,
152
174
  trigger,
153
175
  args,
154
176
  kwargs,
155
- **trigger_kwargs
177
+ **trigger_args
156
178
  )
157
179
 
158
180
  return job
@@ -161,10 +183,9 @@ class Schedule(Base):
161
183
  def modify_task(
162
184
  self,
163
185
  task: Job | str,
164
- trigger: Literal['date', 'interval', 'cron'] | None = None,
165
- args: tuple | None = None,
166
- kwargs: dict | None = None,
167
- **trigger_kwargs: Any
186
+ plan: dict[str, Any],
187
+ *args: Any,
188
+ **kwargs: Any
168
189
  ) -> None:
169
190
  """
170
191
  Modify task.
@@ -172,35 +193,40 @@ class Schedule(Base):
172
193
  Parameters
173
194
  ----------
174
195
  task : Task instance or ID.
175
- trigger : Trigger type.
196
+ plan : Plan trigger keyword arguments.
176
197
  args : Task position arguments.
177
198
  kwargs : Task keyword arguments.
178
- trigger_kwargs : Trigger keyword arguments.
179
199
  """
180
200
 
181
- # Arguments.
182
- params_arg = {}
183
- if args is not None:
184
- params_arg['args'] = args
185
- if kwargs is not None:
186
- params_arg['kwargs'] = kwargs
201
+ # Handle parameter.
202
+ if type(task) == Job:
203
+ task = task.id
204
+ if plan is None:
205
+ plan = {}
206
+ trigger = plan.get('trigger')
207
+ trigger_args = {
208
+ key: value
209
+ for key, value in plan.items()
210
+ if key != 'trigger'
211
+ }
187
212
 
188
- ## Modify.
189
- if params_arg != {}:
190
- self.scheduler.modify_job(
191
- task.id,
192
- **params_arg
213
+ # Modify trigger.
214
+ if plan != {}:
215
+ self.scheduler.reschedule_job(
216
+ task,
217
+ trigger=trigger,
218
+ **trigger_args
193
219
  )
194
220
 
195
- # Trigger.
221
+ # Modify arguments.
196
222
  if (
197
- trigger is not None
198
- or trigger_kwargs != {}
223
+ args != ()
224
+ or kwargs != {}
199
225
  ):
200
- self.scheduler.reschedule_job(
226
+ self.scheduler.modify_job(
201
227
  task.id,
202
- trigger=trigger,
203
- **trigger_kwargs
228
+ args=args,
229
+ kwargs=kwargs
204
230
  )
205
231
 
206
232
 
@@ -270,4 +296,279 @@ class Schedule(Base):
270
296
  self.scheduler.resume_job(id_)
271
297
 
272
298
 
299
+ def build_db(self) -> None:
300
+ """
301
+ Check and build all standard databases and tables, by `self.db_names`.
302
+ """
303
+
304
+ # Check.
305
+ if self.database is None:
306
+ throw(ValueError, self.database)
307
+
308
+ # Set parameter.
309
+
310
+ ## Database.
311
+ databases = [
312
+ {
313
+ 'name': self.db_names['worm']
314
+ }
315
+ ]
316
+
317
+ ## Table.
318
+ tables = [
319
+
320
+ ### 'douban_media'.
321
+ {
322
+ 'path': (self.db_names['base'], self.db_names['base.schedule']),
323
+ 'fields': [
324
+ {
325
+ 'name': 'create_time',
326
+ 'type': 'datetime',
327
+ 'constraint': 'NOT NULL DEFAULT CURRENT_TIMESTAMP',
328
+ 'comment': 'Record create time.'
329
+ },
330
+ {
331
+ 'name': 'update_time',
332
+ 'type': 'datetime',
333
+ 'constraint': 'DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP',
334
+ 'comment': 'Record update time.'
335
+ },
336
+ {
337
+ 'name': 'id',
338
+ 'type': 'int unsigned',
339
+ 'constraint': 'NOT NULL',
340
+ 'comment': 'Douban media ID.'
341
+ },
342
+ {
343
+ 'name': 'imdb',
344
+ 'type': 'char(10)',
345
+ 'comment': 'IMDb ID.'
346
+ },
347
+ {
348
+ 'name': 'type',
349
+ 'type': 'varchar(5)',
350
+ 'constraint': 'NOT NULL',
351
+ 'comment': 'Media type.'
352
+ },
353
+ {
354
+ 'name': 'name',
355
+ 'type': 'varchar(50)',
356
+ 'constraint': 'NOT NULL',
357
+ 'comment': 'Media name.'
358
+ },
359
+ {
360
+ 'name': 'year',
361
+ 'type': 'year',
362
+ 'constraint': 'NOT NULL',
363
+ 'comment': 'Release year.'
364
+ },
365
+ {
366
+ 'name': 'desc',
367
+ 'type': 'varchar(1000)',
368
+ 'comment': 'Media content description.'
369
+ },
370
+ {
371
+ 'name': 'score',
372
+ 'type': 'float',
373
+ 'comment': 'Media score, [0,10].'
374
+ },
375
+ {
376
+ 'name': 'score_count',
377
+ 'type': 'int',
378
+ 'comment': 'Media score count.'
379
+ },
380
+ {
381
+ 'name': 'minute',
382
+ 'type': 'smallint',
383
+ 'comment': 'Movie or TV drama episode minute.'
384
+ },
385
+ {
386
+ 'name': 'episode',
387
+ 'type': 'smallint',
388
+ 'comment': 'TV drama total episode number.'
389
+ },
390
+ {
391
+ 'name': 'episode_now',
392
+ 'type': 'smallint',
393
+ 'comment': 'TV drama current episode number.'
394
+ },
395
+ {
396
+ 'name': 'premiere',
397
+ 'type': 'json',
398
+ 'comment': 'Premiere region and date dictionary.'
399
+ },
400
+ {
401
+ 'name': 'country',
402
+ 'type': 'json',
403
+ 'comment': 'Release country list.'
404
+ },
405
+ {
406
+ 'name': 'class',
407
+ 'type': 'json',
408
+ 'comment': 'Class list.'
409
+ },
410
+ {
411
+ 'name': 'director',
412
+ 'type': 'json',
413
+ 'comment': 'Director list.'
414
+ },
415
+ {
416
+ 'name': 'star',
417
+ 'type': 'json',
418
+ 'comment': 'Star list.'
419
+ },
420
+ {
421
+ 'name': 'scriptwriter',
422
+ 'type': 'json',
423
+ 'comment': 'Scriptwriter list.'
424
+ },
425
+ {
426
+ 'name': 'language',
427
+ 'type': 'json',
428
+ 'comment': 'Language list.'
429
+ },
430
+ {
431
+ 'name': 'alias',
432
+ 'type': 'json',
433
+ 'comment': 'Alias list.'
434
+ },
435
+ {
436
+ 'name': 'comment',
437
+ 'type': 'json',
438
+ 'comment': 'Comment list.'
439
+ },
440
+ {
441
+ 'name': 'image',
442
+ 'type': 'varchar(150)',
443
+ 'constraint': 'NOT NULL',
444
+ 'comment': 'Picture image URL.'
445
+ },
446
+ {
447
+ 'name': 'image_low',
448
+ 'type': 'varchar(150)',
449
+ 'constraint': 'NOT NULL',
450
+ 'comment': 'Picture image low resolution URL.'
451
+ },
452
+ {
453
+ 'name': 'video',
454
+ 'type': 'varchar(150)',
455
+ 'comment': 'Preview video Douban page URL.'
456
+ }
457
+ ],
458
+ 'primary': 'id',
459
+ 'indexes': [
460
+ {
461
+ 'name': 'n_create_time',
462
+ 'fields': 'create_time',
463
+ 'type': 'noraml',
464
+ 'comment': 'Record create time normal index.'
465
+ },
466
+ {
467
+ 'name': 'n_update_time',
468
+ 'fields': 'update_time',
469
+ 'type': 'noraml',
470
+ 'comment': 'Record update time normal index.'
471
+ },
472
+ {
473
+ 'name': 'u_imdb',
474
+ 'fields': 'imdb',
475
+ 'type': 'unique',
476
+ 'comment': 'IMDb number unique index.'
477
+ },
478
+ {
479
+ 'name': 'n_name',
480
+ 'fields': 'name',
481
+ 'type': 'noraml',
482
+ 'comment': 'Media name normal index.'
483
+ }
484
+ ],
485
+ 'comment': 'Douban media information table.'
486
+ }
487
+
488
+ ]
489
+
490
+ ## View stats.
491
+ views_stats = [
492
+
493
+ ### 'schedule_stats'.
494
+ {
495
+ 'path': (self.db_names['base'], self.db_names['base.schedule_stats']),
496
+ 'items': [
497
+ {
498
+ 'name': 'count',
499
+ 'select': (
500
+ 'SELECT COUNT(1)\n'
501
+ f'FROM `{self.db_names['worm']}`.`{self.db_names['worm.douban_media']}`'
502
+ ),
503
+ 'comment': 'Media count.'
504
+ },
505
+ {
506
+ 'name': 'past_day_count',
507
+ 'select': (
508
+ 'SELECT COUNT(1)\n'
509
+ f'FROM `{self.db_names['worm']}`.`{self.db_names['worm.douban_media']}`\n'
510
+ 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) = 0'
511
+ ),
512
+ 'comment': 'Media count in the past day.'
513
+ },
514
+ {
515
+ 'name': 'past_week_count',
516
+ 'select': (
517
+ 'SELECT COUNT(1)\n'
518
+ f'FROM `{self.db_names['worm']}`.`{self.db_names['worm.douban_media']}`\n'
519
+ 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 6'
520
+ ),
521
+ 'comment': 'Media count in the past week.'
522
+ },
523
+ {
524
+ 'name': 'past_month_count',
525
+ 'select': (
526
+ 'SELECT COUNT(1)\n'
527
+ f'FROM `{self.db_names['worm']}`.`{self.db_names['worm.douban_media']}`\n'
528
+ 'WHERE TIMESTAMPDIFF(DAY, `create_time`, NOW()) <= 29'
529
+ ),
530
+ 'comment': 'Media count in the past month.'
531
+ },
532
+ {
533
+ 'name': 'avg_score',
534
+ 'select': (
535
+ 'SELECT ROUND(AVG(`score`), 1)\n'
536
+ f'FROM `{self.db_names['worm']}`.`{self.db_names['worm.douban_media']}`'
537
+ ),
538
+ 'comment': 'Media average score.'
539
+ },
540
+ {
541
+ 'name': 'score_count',
542
+ 'select': (
543
+ 'SELECT FORMAT(SUM(`score_count`), 0)\n'
544
+ f'FROM `{self.db_names['worm']}`.`{self.db_names['worm.douban_media']}`'
545
+ ),
546
+ 'comment': 'Media score count.'
547
+ },
548
+ {
549
+ 'name': 'last_create_time',
550
+ 'select': (
551
+ 'SELECT MAX(`create_time`)\n'
552
+ f'FROM `{self.db_names['worm']}`.`{self.db_names['worm.douban_media']}`'
553
+ ),
554
+ 'comment': 'Media last record create time.'
555
+ },
556
+ {
557
+ 'name': 'last_update_time',
558
+ 'select': (
559
+ 'SELECT IFNULL(MAX(`update_time`), MAX(`create_time`))\n'
560
+ f'FROM `{self.db_names['worm']}`.`{self.db_names['worm.douban_media']}`'
561
+ ),
562
+ 'comment': 'Media last record update time.'
563
+ }
564
+ ]
565
+
566
+ }
567
+
568
+ ]
569
+
570
+ # Build.
571
+ self.database.build.build(databases, tables, views_stats=views_stats)
572
+
573
+
273
574
  __iter__ = tasks
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: reykit
3
- Version: 1.1.66
3
+ Version: 1.1.67
4
4
  Summary: Kit method set.
5
5
  Project-URL: homepage, https://github.com/reyxbo/reykit/
6
6
  Author-email: Rey <reyxbo@163.com>
@@ -11,7 +11,7 @@ reykit/rnum.py,sha256=PhG4V_BkVfCJUsbpMDN1umGZly1Hsus80TW8bpyBtyY,3653
11
11
  reykit/ros.py,sha256=Yi_mfYzVHwjjUZke0BNX7PKFLZpoT5NRyj5aDOSNQRU,46911
12
12
  reykit/rrand.py,sha256=4VwooITgox54_GonELcJfcIpStDi-UJchpnyWKnyeIA,8606
13
13
  reykit/rre.py,sha256=1qva7xatKVE9qC2j7IujjXSM59qxHWwTYpiizFFQ8Xo,6024
14
- reykit/rschedule.py,sha256=QakEAtOcMg8uL2iOLre9uSsH-DsW6uAvzdXFiPJw_1o,5767
14
+ reykit/rschedule.py,sha256=UfvreJ3QBvXxZAJOPZosI4Zp5isPOdgPn-oLTFeLD0k,17238
15
15
  reykit/rstdout.py,sha256=yesWo7wIGablpyAu-2J2Gw11Qp3GdQjGICTyIcvLyt4,8200
16
16
  reykit/rsys.py,sha256=AP62KyN40flCeQJBclfJq8shachSAFT0LkVjiKsXkrw,24946
17
17
  reykit/rtable.py,sha256=UQ-JlwjssMR3gY1iY-VGQEKQ5_BZabpJy6TL7Fx19c4,12200
@@ -22,7 +22,7 @@ reykit/rwrap.py,sha256=FEmeK_fboJ-OyXeJf8bilc7U2ph8xIbZGNHb6fLCy2c,15063
22
22
  reykit/rzip.py,sha256=BGEONswuBZxQ-zcgd_xp2fcvYesC9AmKaaXWvnT3bTI,3456
23
23
  reykit/rdll/__init__.py,sha256=nLSb8onBm2ilyoxzpDzUeGfSCKwkLEesIhzK3LiJ8mk,701
24
24
  reykit/rdll/rdll_core.py,sha256=o6-rKcTQgxZQe0kD3GnwyNb3KL9IogzgCQNOmYLMm7A,5086
25
- reykit-1.1.66.dist-info/METADATA,sha256=TXCGGPt494VS6fF95ogjAuJovsm4kqc-TCMinRBG-X8,1872
26
- reykit-1.1.66.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
- reykit-1.1.66.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
28
- reykit-1.1.66.dist-info/RECORD,,
25
+ reykit-1.1.67.dist-info/METADATA,sha256=BGZWI7zUlK8aPG0rOlVMJENMKp12uK86Q6cYaLYaDE4,1872
26
+ reykit-1.1.67.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
27
+ reykit-1.1.67.dist-info/licenses/LICENSE,sha256=UYLPqp7BvPiH8yEZduJqmmyEl6hlM3lKrFIefiD4rvk,1059
28
+ reykit-1.1.67.dist-info/RECORD,,