-
Notifications
You must be signed in to change notification settings - Fork 0
/
SDA_SS_to_MyBible_devotions.py
executable file
·1190 lines (1090 loc) · 57.6 KB
/
SDA_SS_to_MyBible_devotions.py
1
2
3
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#! /usr/bin/env python3
# encoding: utf-8
""" SDA Sabbath school MyBible module generator
This program creates MyBible devotions modules from data from adventech.io
"""
import os
import argparse
import datetime
import regex
import sqlite3
import requests
import json
import importlib
import sys
import unicodedata
from bs4 import BeautifulSoup
#from sqlalchemy.sql.expression import false
#import bible_codes
DEBUG_LEVEL = 0
class text_material:
"""some text material: text for the day, intro, comments
not realized"""
def __init__(self):
self.content = ""
self.title = ""
# web link of a material
self.full_path = ""
# xml content of a material, text with attached information
self.content = ""
# title of material for a material
self.title = ""
# language code of a material
self.lang_code = "ru"
# path to local storage of content
self.path_to_cache = "./"
def set_lang_code(self, lang):
"""
sets language for the material.
Parameters
----------
lang: str
two-letter code of language of a day, for example 'en', 'ru', etc.
"""
self.lang_code = lang
def content_extract(self, path_to_content: str, force = False):
"""
checks if material from path_to_content is available in the local storage
extracts content of material from local storage or from server
if parameter force is True then extracts from server
"""
#print("content_extract {0}".format(path_to_content))
path_local = path_to_content
# remove https from root directory name
if path_local.startswith("https://") :
path_local = path_local[len("https://"):]
# concatenate path to cache and path to extracted file within the cache
path_to_file = self.path_to_cache + '/' + path_local
# check if file already exists in the cache ?
if (os.path.isfile(path_to_file) and not(force)):
# if file already exist then read from the local file
with open(path_to_file) as inp_file:
self.r_json = json.load(inp_file)
else:
# get data and safe to the local file
request_str = (path_to_content)
# send request and get xml with day of lesson with its attributes
r = requests.get(request_str)
# extract content, date and title
folder_path = os.path.dirname(path_to_file)
if not os.path.exists(folder_path):
os.makedirs(folder_path)
#print("Directory " , folder_path , " Created ")
with open(path_to_file, 'w') as out_file:
json.dump(r.json(), out_file)
self.r_json = r.json()
def get_content(self, block):
pass
def get_title(self, block):
pass
class intro(text_material):
"""intro for quarter"""
def get_content(self, quarter):
pass
def __init__(self):
self.text = ""
class day(text_material):
"""material for the day of the lesson (includes text material)
Class gathers and stores content of a lesson for a particular day.
Attributes
----------
day_N: int
day number of the lesson to handle, should be from 1 to 7
day_date: datetime
date of the lesson to handle
full_path: str
path to lesson, it is a link https:// ...
content: str
content of reading for a day
title: str
title of reading for a day
lang_code: str
two-letters code of language of a day, for example 'en', 'ru', 'uk' etc.
"""
def get_content(self, full_path: str, day_N: int):
"""get reading for day
method calls http request,
parses and extracts content and title of reading for day.
Parameters
----------
full_path: str
path to lesson, it is a link https:// ...
day_N: int
number of day of the lesson to handle, should be from 1 to 7"""
# set values of attributes to values of parameters passed to function
self.full_path = full_path
self.day_N = day_N
self.content_extract("{0}/days/{1:02}/read/index.json".format(self.full_path, self.day_N))
self.content = self.r_json.get('content')
# convert content of the day into format of MyBible
self.content = adventech_lesson_to_MyBibe_lesson( self.content,
self.lang_code)
self.day_date = datetime.datetime.strptime(self.r_json.get('date'),
"%d/%m/%Y")
# extract title from xml and save to attribute
self.title = self.r_json.get('title')
# create a link for request by adding tail with link to day to the lesson
class comment(text_material):
"""commentaries for lesson, for sabbath schoo leaders
not realized yet, sources of commentaries needed"""
def __init__(self):
text_material.__init__(self)
def get_content(self, full_path):
"""get commentary for the lesson
method calls http request,
parses and extracts content and title of reading for day.
Parameters
----------
full_path: str
path to lesson, it is a link https:// ...
day_N: int
number of day of the lesson to handle, should be from 1 to 7"""
# set values of attributes to values of parameters passed to function
self.full_path = full_path
# create a link for request by adding tail with link to day to the lesson
self.content_extract(self.full_path + '/' + "days/teacher-comments/read/index.json")
self.content = self.r_json.get('content')
# convert content of the day into format of MyBible
self.content = adventech_lesson_to_MyBibe_lesson( self.content,
self.lang_code)
self.day_date = datetime.datetime.strptime(self.r_json.get('date'),
"%d/%m/%Y")
# extract title from xml and save to attribute
self.title = self.r_json.get('title')
pass
class lesson(text_material):
"""lesson (includes days)
class gathers a links for the lessons of the quarter, title of lesson, dates of beginning and end
Attributes
----------
lesson_N: int
number of lesson in quarter
lesson_title: str
title of lesson
lesson_start, lesson_end: datetime
date of the beginning and the end of the lesson
lesson_full_path: str
link to lesson, https:// ...
lesson_index: str
index to lesson in form of adventech.io index
lesson_block: str
response of http request with lesson
days[]: day
array of days
lang_code: str
two-letters code of language of a day, for example 'en', 'ru', 'uk' etc.
"""
def get_lesson_N(self):
""" returns number of the lesson in the quarter """
# print("lesson id {0}".format(lesson_block.get("id")))
return self.lesson_block.get("id")
def get_lesson_start(self):
""" returns date of beginning of the lesson """
start_string = self.lesson_block.get("start_date")
# print("start_string {0}".format(start_string))
return datetime.date(int(start_string.split('/')[2]), \
int(start_string.split('/')[1]), \
int(start_string.split('/')[0]))
def get_lesson_end(self):
""" returns date of end of the lesson """
end_string = self.lesson_block.get("end_date")
# print("end_string {0}".format(start_string))
return datetime.date(int(end_string.split('/')[2]), \
int(end_string.split('/')[1]), \
int(end_string.split('/')[0]))
def get_lesson_full_path(self):
""" returns web-address of the lesson"""
return self.lesson_block.get("full_path")
def get_lesson_index(self):
""" returns index of web-address of the lesson """
return self.lesson_block.get("index")
def get_lesson_title(self):
return self.lesson_block.get("title")
def get_content(self):
""" extracts data for the lesson
fills attributes of the lesson
gathers data of each day in the lesson"""
self.lesson_N = int(self.get_lesson_N())
self.lesson_title = self.get_lesson_title()
self.lesson_start = self.get_lesson_start()
self.lesson_end = self.get_lesson_end ()
self.lesson_full_path = self.get_lesson_full_path ()
self.lesson_index = self.get_lesson_index()
for day_N in range(1, 8):
# prints number of current lesson and number of current day,
# it is useful for progress indication
#print("get lesson {0:2} day {1} {2}/index.json".format(self.lesson_N, day_N, self.lesson_full_path))
print("get lesson {0:2} day {1} {2}/days/{1:02}/read/index.json".format(self.lesson_N, day_N, self.lesson_full_path))
#self.content_extract("{0}/days/{1:02}/read/index.json".format(self.full_path, self.day_N))
# creates a new object for the day
curr_day = day()
# sets language of the day same as language of the lesson
curr_day.set_lang_code(self.lang_code)
# adds current day to the array of days for the lesson
self.days.append(curr_day)
# gathers day content for current day,
# method is called from the object placed in array of days
# current day now is the last day in array
self.days[-1].get_content(self.lesson_full_path, day_N)
self.content_extract("{0}/index.json".format(self.lesson_full_path))
# extract content, date and title
self.lessons_info = self.r_json.get('days')
# check if lesson includes commentary?
# search for id='teacher-comments'
for lesson_info_item in self.lessons_info:
if lesson_info_item.get('id') == 'teacher-comments':
# create comment object and process comment
self.lesson_comment = comment()
self.lesson_comment.set_lang_code(self.lang_code)
self.lesson_comment.get_content(self.lesson_full_path)
break
def __init__(self, lesson_block, lesson_N):
""" create object of lesson
Parameters
----------
lesson_block: text with xml
response of adventech.io API for lesson request
lesson_N: int
number of lesson in quarter
"""
text_material.__init__(self)
self.lesson_N = 0
self.lesson_title = ""
self.lesson_start = datetime.date(2000, 1, 1)
self.lesson_end = datetime.date(2000, 1, 1)
# web address of a lesson
self.lesson_full_path = ""
# index of a lesson in web-address
self.lesson_index = ""
# xml with lessons parameters
self.lesson_block = ""
# array of day-object of this lesson
self.days = []
self.lesson_comment = None
# language of lesson
self.lang_code = "ru"
self.lesson_block = lesson_block
self.lesson_N = lesson_N
class quarter(text_material):
"""quarter (includes lessons)"""
def set_quarter(self, year, quarter):
""" sets year of the lesson and the quarter number in the year"""
self.year = year
self.quart_N = quarter
def set_lesson_type(self, lesson_type):
""" sets lesson type
Parameters
----------
lesson_type: string
- ad - adults
- ay - youth
- ... as in adventech.io
"""
self.lesson_type = lesson_type
def set_db_cursor(self, db_cursor):
""" set database cursor
sets database cursor to access database
to read existing information to add new quarter
to write new data """
self.db_cursor = db_cursor
def get_content(self):
""" gets content of the quarter
prepares gathering data for a lesson and calls gathering methods
"""
# sets lesson type code for adventech.io request
lesson_type_string = ""
if self.lesson_type == "ad":
lesson_type_string = ""
else:
if self.lesson_type == "ay":
lesson_type_string = "-ay"
# combines request of quarter from parameters
request_str = ("{0}/api/v1/{1}/quarterlies/{2}-{3:02}{4}/index.json")\
.format(self.site, self.lang_code, self.year,
self.quart_N, lesson_type_string)
print("get_content by {0}".format(request_str))
# sends request to adventech.io
self.content_extract(request_str)
# extracts from xml received in response title of lesson, description
self.quarter_title = self.r_json.get('quarterly').get('title')
self.quarter_description = self.r_json.get('quarterly').get('description')
print("quarter {0}-{1:02}{2}"\
.format(self.year, self.quart_N, lesson_type_string))
print("*** title: {0}".format(self.quarter_title))
# extracts lessons from xml received in response
self.lessons_block = self.r_json.get('lessons')
# enumerates lessons in lessons_block to get content from each lesson
for index, lesson_block in enumerate(self.lessons_block):
lesson_block = self.lessons_block[index]
# creates new lesson and append to array of lessons
self.lessons_set.append(lesson(lesson_block, index + 1))
# sets language of a new lesson
self.lessons_set[-1].set_lang_code(self.lang_code)
# receives content for the new lesson
self.lessons_set[-1].get_content()
pass
def print_quarter(self):
""" prints information about quarter, for debugging """
for index, _ in enumerate(self.lessons_set):
print("lesson_set[{0}] {1}"\
.format(index, self.lessons_set[index].lesson_N))
print("lesson_set[{0}] {1}"\
.format(index, self.lessons_set[index].lesson_title))
print("lesson_set[{0}] {1}"\
.format(index, self.lessons_set[index].lesson_start))
print("lesson_set[{0}] {1}"\
.format(index, self.lessons_set[index].lesson_end))
print("lesson_set[{0}] {1}"\
.format(index, self.lessons_set[index].lesson_full_path))
print("lesson_set[{0}] {1}"\
.format(index, self.lessons_set[index].lesson_index))
for day in self.lessons_set[index].days:
print("day {0} is {1}".format(day.day_N, day.content))
# TODO: remove this method
def create_table_info_x(self, cursor, year, quart, name, lang):
""" creates table info in database, deprecated """
ret_val = 0
origin_text = "'created by Egor Ibragimov, [email protected]\n" + \
" the text is taken from sabbath-school.adventech.io'"
history_of_changes_text = ""
language_text = "'{0}'".format(lang)
description_text = \
"'Seventh Day Adventist Church`s Sabbath School lesson {0}-{1}'"\
.format(year, quart)
detailed_info_text = ""
exec_string = '''CREATE TABLE IF NOT EXISTS info ( name text, value text)'''
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
cursor.execute(exec_string)
exec_string = "INSERT INTO info VALUES ( 'origin', {0} )".format(origin_text)
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
cursor.execute(exec_string)
exec_string = """INSERT INTO info VALUES ( 'description', {0} )""".format(description_text)
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
cursor.execute(exec_string)
exec_string = "INSERT INTO info VALUES ( 'language', {0} )".format(language_text)
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
cursor.execute(exec_string)
return(ret_val)
def __init__(self):
"""the constructor"""
print("create : quarter :")
text_material.__init__(self)
self.site = "https://sabbath-school.adventech.io"
self.year = 0
self.quart_N = 0
self.lang_code = "ru"
self.lesson_type = "ad"
self.lessons_block = None
self.lessons_set = []
self.quarter_title = ""
self.quarter_description = ""
self.db_cursor = None
pass
class SS_year(text_material):
""" year class (includes quarters)
"""
def set_lang_code(self, lang):
self.lang_code = lang
def set_lesson_type(self, lesson_type):
""" set lesson type
Parameters
----------
lesson_type: string
- ad - adults
- ay - youth
- ... as in adventech.io
"""
self.lesson_type = lesson_type
def set_year(self, year):
self.year = year
def quarters_list_get(self):
""" gets a list of quarters from the server """
self.quarters_list_year = []
self.quarters_list_titles_all_available_for_year = []
# creates request to adventech.io from attributes
request_str = ("{0}/api/v1/{1}/quarterlies/index.json")\
.format(self.site, self.lang_code, self.year)
print("quarters_list_get with {0}".format(request_str))
# sends request to the server
self.content_extract(request_str, force=True)
# selects quarters for the year and appends it to the quarters array
# if lesson type is adult then replace 'ad' with empty string, because for lessons for adult ther is no suffix
lesson_type_index = self.lesson_type if not(self.lesson_type == 'ad') else ''
for index, quart in enumerate(self.r_json):
quart = self.r_json[index]
# select quarters for this year
if (int(quart.get("id").split("-")[0]) == self.year):
#print("{0} - {1}".format(quart.get("id")[8:10], lesson_type_index))
if (quart.get("id")[8:] == lesson_type_index):
#print("quart {0} {1}".format(quart.get('id'), quart.get('title')))
self.quarters_list_titles_all_available_for_year.append(
(quart.get('id'), quart.get('title'),
quart.get('description')))
if (int(quart.get("id").split("-")[0])
== self.year and len(quart.get("id").split("-")) == 2):
self.quarters_list_year.append(quart)
print(" quarters available for selected year: ")
# sorts quarters in array by quarter number
self.quarters_list_year.sort(key = lambda quart_rec:
quart_rec.get("id").split("-")[1])
self.quarters_list_titles_all_available_for_year.sort(
key = lambda quart_rec: quart_rec[0].split("-")[1])
for quart in self.quarters_list_year:
print("quarter {0}".format(quart.get("id")))
print()
def get_content(self):
""" gets content of the year
creates quarter objects with appropriate parameters
for every quarter in quarters_list_year
and gets content of every quarter """
print("SS_year: get content")
print("self.quarters {0}".format(self.quarters))
# enumerates quarters elements in quarters_list_year
for quart in self.quarters_list_year:
print("process quarter {0}".format(quart.get("id")))
# creates a new quarter object
self.quarters.append(quarter())
# sets parameters of the quarter: year and the quarter number,
self.quarters[-1].set_quarter(self.year,
int(quart.get("id").split("-")[1]))
# sets language
self.quarters[-1].set_lang_code(self.lang_code)
# sets lesson type
self.quarters[-1].set_lesson_type(self.lesson_type)
# sends requests chain to get content of each day
self.quarters[-1].get_content()
def __init__(self):
text_material.__init__(self)
self.quarters = []
self.site = "https://sabbath-school.adventech.io"
self.lang_code = "ru"
self.lesson_type = ''
self.quarters_list_year = []
self.year = 0
class db_MyBible_devotions_SS:
""" database with devotions (one devotion - one year) """
def set_lang_code(self, lang):
self.lang_code = lang
self.SS_year_inst.set_lang_code(lang)
def set_lesson_type(self, lesson_type):
self.lesson_type = lesson_type
self.SS_year_inst.set_lesson_type(self.lesson_type)
def set_year(self, year):
self.year = year
self.SS_year_inst.set_year(self.year)
def get_def_file_name(self):
""" returns default file name of the database """
lesson_type_index = self.lesson_type
if (self.lesson_type == ''):
lesson_type_index = 'ad'
file_name = "SS-{0}-{1}'{2}.devotions.SQLite3"\
.format(self.lang_code, lesson_type_index, str(self.year)[2:4])
return file_name
def connect_to_db(self, file_name):
""" opens the database in file_name and checks if it is an appropriate devotions"""
# checks year passed in parameters
if (self.year >= 1852 and self.year <= 2099):
# if file name is empty it uses default file name
if (file_name == ""):
self.file_name = self.get_def_file_name()
else:
self.file_name = file_name
# checks if the file with file_name exists
if (os.path.isfile(self.file_name)):
try:
# tries to open file_name as database
self.db_conn = sqlite3.connect(self.file_name)
self.db_cursor = self.db_conn.cursor()
#print("connection : {0} ; cursor : {1}"\
# .format(self.db_conn, self.db_cursor))
#print("sqlite3 version {0}".format(sqlite3.version))
ret_val = 1
except sqlite3.Error as e:
print(e)
err_name = "creating the database ends with error {0}".format(e)
ret_val = -1
if (ret_val == 1):
#processes input file
try:
# checks is it SDA Sabbath School devotions
self.db_cursor.execute("SELECT * FROM info WHERE name = 'description'")
info_description = self.db_cursor.fetchall()
# checks description
if (info_description[0][1].startswith(bible_codes.db_info_description_title)):
self.db_inp_file_is_SDA_SS_devotions = True
print("it is the SDA Sabbath School devotion database")
# finds the last quarter in the database
self.db_cursor.execute("SELECT * FROM devotions WHERE devotion LIKE '<h3>%'")
devotion_quart_heads = self.db_cursor.fetchall()
for _, value in enumerate(devotion_quart_heads):
(self.db_end_year, self.db_end_quart) = value[1][4:10].split('-')
self.db_end_year = int(self.db_end_year)
self.db_end_quart = int(self.db_end_quart)
print("db_end is {0} - {1}".format(self.db_end_year, self.db_end_quart))
# finds the last day in the database
self.db_cursor.execute("SELECT MAX(day) FROM devotions")
self.db_last_day = self.db_cursor.fetchall()[0][0]
print("the last day in the database is {0}".format(self.db_last_day))
if (not(self.db_end_year == self.year)):
ret_val = -3
print("year inconsistent, passed through arguments: {0}, in database of input file {1}"\
.format(self.year, self.db_end_year))
except sqlite3.Error as e:
ret_val = -2
print("error in processing SELECT info from database : {0}", e)
print("file will be cleared and created from the beginning")
self.db_end_year = -1
self.db_end_quart = -1
else:
print("Error: unable to connect to {0}, file not exist".format(self.file_name))
ret_val = -4
self.err_name = "file not exist"
else:
print("set correct year 1852...2099, {0} is outside".format(self.year))
ret_val = -5
self.err_name = "incorrect year"
return(ret_val)
def create_db(self, file_name):
""" creates a new database with devotions
in file with name file_name if field file_name not empty
or use default file name """
# checks year
if (self.year >= 1852 and self.year <= 2099):
# if file_name is empty then default file name will be used
if (file_name == ""):
self.file_name = self.get_def_file_name()
else:
self.file_name = file_name
print("creating the database with the file name {0}".format(self.file_name))
# checks if file with file_name is exists
if (not(os.path.isfile(self.file_name))):
# creates file with name file_name with database
try:
self.db_conn = sqlite3.connect(self.file_name)
self.db_cursor = self.db_conn.cursor()
print("connection : {0} ; cursor : {1}"\
.format(self.db_conn, self.db_cursor))
print("sqlite3 version {0}".format(sqlite3.version))
ret_val = 1
except sqlite3.Error as e:
print(e)
self.err_name = "error while creating database : {0}".format(e)
ret_val = -1
else:
# if file file_name exists, then returns with error
print("Error: file already exists")
self.err_name = "file already exists"
ret_val = -4
else:
print("set correct year 1852...2099, please, {0} is outside".format(self.year))
ret_val = -5
self.err_name = "incorrect year"
return(ret_val)
def get_year(self):
""" returns list of quarters in the year """
self.SS_year_inst.set_year(self.year)
self.SS_year_inst.set_lang_code(self.lang_code)
self.SS_year_inst.set_lesson_type(self.lesson_type)
self.SS_year_inst.quarters_list_get()
def get_content(self):
""" sends chain of requests to get content of days """
self.SS_year_inst.get_content()
def get_db_description_text(self):
"""
returns description text
function returns description text for the database file of the lesson in selected language
it substitutes
- the name of material - Seventh-day Adventist Church`s Sabbath School lessons
- the year and the quarter
- the version: for adults or for youth
"""
# sets description text in different languages
# default value is English
# sets header of devotion
# sets description of type of lesson
#name_text = bible_codes.db_info_description["en"]
try:
name_text = bible_codes.db_info_description_title
except Exception:
print("unable to get internat.db_info_description for " + self.lang_code)
if (self.lesson_type == 'ad' or self.lesson_type == ''):
version_text = bible_codes.db_info_description_version_adult
else:
if (self.lesson_type == 'ay'):
version_text = version_text = bible_codes.db_info_description_version_youth
else:
version_text = ""
description_text = "{0} {1} {2}".format(name_text, version_text, self.SS_year_inst.quarters_list_year[-1].get('id'))
return description_text
def get_db_detailed_info_text(self):
""" returns detailed info in selected languages """
themes_list = "<p> {0} </p>".format(bible_codes.db_info_description_list_of_quarterly_themes)
for quarter_rec in self.SS_year_inst.quarters_list_titles_all_available_for_year:
themes_list = themes_list + "<h4>" + "{0}.".format(int(quarter_rec[0].split("-")[1])) + " " + quarter_rec[1] + "</h4>"# + "<p>" + quarter.quarter_description + "</p>"
#from_author_of_module_text = bible_codes.from_author['en']
try:
from_author_of_module_text = bible_codes.db_info_description_from_author
except Exception:
print("unable to get internat.from_author for " + self.lang_code)
detailed_info_text = "{0}<br><p>{1}</p>".format(themes_list, from_author_of_module_text)
return detailed_info_text
def create_table_info(self):
""" creates table 'info' for the MyBible devotion database"""
ret_val = 0
origin_text = bible_codes.db_info_description_origin_text
try:
origin_text = bible_codes.db_info_description_origin_text
except Exception:
print("unable to find internat.origin_text for " + self.lang_code)
history_of_changes_text = "2018-06-30 - created"
language_text = "{0}".format(self.lang_code)
detailed_info_text = ""
if self.lang_code == "ru" or self.lang_code == "uk":
russian_numbering_text = "{0}".format('true')
else:
russian_numbering_text = "{0}".format('false')
exec_string = '''CREATE TABLE IF NOT EXISTS info ( name text, value text)'''
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
exec_string = '''DELETE from info'''
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
exec_string = "INSERT INTO info VALUES ( 'origin', '{0}' )".format(origin_text)
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
exec_string = """INSERT INTO info VALUES ( 'description', '{0}' )""".format(self.get_db_description_text())
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
exec_string = """INSERT INTO info VALUES ( 'detailed_info', '{0}' )""".format(self.get_db_detailed_info_text().replace("'", "''"))
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
exec_string = "INSERT INTO info VALUES ( 'language', '{0}' )".format(language_text)
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
exec_string = "INSERT INTO info VALUES ( 'russian_numbering', '{0}' )".format(russian_numbering_text)
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
return(ret_val)
def update_description(self):
""" updates description of the existing database """
exec_string = "DELETE FROM info WHERE name='description'"
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
exec_string = """INSERT INTO info VALUES ( 'description', '{0}' )""".format(self.get_db_description_text())
if DEBUG_LEVEL > 0:
print ("execute db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
def update_detailed_info(self):
""" update detailed_info of the existing database """
exec_string = "DELETE FROM info WHERE name='detailed_info'"
if DEBUG_LEVEL > 0:
print ("executing db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
# in get_db_detailed_info_text() doubling single quotes to avoid errors in processing in SQLite
exec_string = """INSERT INTO info VALUES ( 'detailed_info', '{0}' )""".format(self.get_db_detailed_info_text().replace("'", "''"))
if DEBUG_LEVEL > 0:
print ("executing db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
def create_table_devotions(self):
""" writes days from gathered days in lessons in quarters in year into the database"""
exec_string = '''CREATE TABLE IF NOT EXISTS devotions (day NUMERIC, devotion TEXT)'''
if DEBUG_LEVEL > 0:
print ("executing db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
exec_string = '''CREATE UNIQUE INDEX IF NOT EXISTS devotions_index ON devotions (day ASC)'''
if DEBUG_LEVEL > 0:
print ("executing db : {0}".format(exec_string))
self.db_cursor.execute(exec_string)
# the day after the last day in the database is the first day of current quarter
# set days_counter to number of the last day in the database plus 1
days_counter = self.db_last_day + 1
days_accumulator = ""
print("the number of the quarters is {0}".format(len(self.SS_year_inst.quarters)))
for quarter in self.SS_year_inst.quarters:
lesson_counter = 1
print("the length of the lesson_set array is {0}".format(len(quarter.lessons_set)))
for lesson in quarter.lessons_set:
print("lesson N {0:2} - {1:2}".format(lesson_counter, lesson.lesson_N))
# days_accumulator concatenates html for the current day
# in the beginning and in the end of the year it is possible that days are considers days of current quarter from the year before and the year after the current year
#days_accumulator = ""
for day in lesson.days:
# day starts from a lesson number and a header
day_content_handled = "<p> {0} № {1} {2} {3}</p> <h4>{4}</h4> {5}".format(
bible_codes.db_info_description_lesson, str(lesson.lesson_N),
bible_codes.db_info_description_day, str(day.day_N), day.title, day.content)
if (day.day_N == 7):
if not(lesson.lesson_comment == None):
day_content_handled += "<h4>{0}</h4>{1}".format(lesson.lesson_comment.title, lesson.lesson_comment.content)
exec_string = '''INSERT INTO devotions VALUES ( ?, ? )'''
if DEBUG_LEVEL > 0:
print ("executing db : {0}".format(exec_string))
# if it is the first day in the quarter then add a quarter title and a quarter description
if (lesson.lesson_N == 1 and day.day_N == 1):
days_accumulator = "<h3>" + "{0}-{1}".format(quarter.year, quarter.quart_N) + " " + quarter.quarter_title + "</h3>" + "<p>" + quarter.quarter_description + "</p>" + days_accumulator
days_accumulator = days_accumulator + day_content_handled
# if the current day is not in current year then keep current day in the days_accumulator
if ((day.day_date.year == self.year)):# or 1): #fix for bug in sources of lessons at 2021q2
# if the current day is in current year then put day into database and clear days_accumulator
print( "put day {0}: ".format(days_counter))
self.db_cursor.execute(exec_string, (days_counter, days_accumulator))
days_accumulator = ""
days_counter += 1
else:
print("move the day {0} to the next day".format(day.day_date))
lesson_counter += 1
pass
def close_db(self):
if (self.db_conn):
print("close database")
self.db_conn.commit()
self.db_conn.close()
def __init__(self):
# default parameters
self.lang_code = 'ru' # language code for sabbath school text
self.lesson_type = '' # type of lesson: adult, youth etc.
self.SS_year_inst = SS_year() # year class object
self.year = 0
# name of the database file
self.file_name = ""
# year in the database
self.db_end_year = -1
# the last quarter in the database
self.db_end_quart = -1
# the variable stores the result of the check if the file
# with the name file_name is the file with devotions
self.db_inp_file_is_SDA_SS_devotions = False
# the last day in the database
self.db_last_day = 0
self.year = 0
self.err_name = ""
self.file_name = ""
self.db_conn = None
self.db_cursor = None
def __del__(self):
self.close_db()
def adventech_ref_to_MyBible_ref(lang_code, doc, inp_tag):
""" find bible references and convert to MyBible format"""
# regular expression for selecting reference to book name with verses in particular book
# / book name \ / head, verse repeatable after selected book name \
find_refs = regex.compile(r"(?:\d\s*)?(?:[\p{Lu}]\.\s)?[\p{Lu}]?[\p{Ll}\p{M}\’\']+\.?\s*(?:\d+(?:[\:\-\,]\d+)?(?:\s*[\-\,]\s*\d+)?(?::\d+|(?:\s*[\p{Lu}]?[\p{Ll}\’\']+\s*\d+:\d+))?(?:\s*)?)*")
# regular expression for selecting book name from reference with book name, head and verse
parse_ref = regex.compile(r"(?:\d\s*)?(?:[\p{Lu}]\.\s)?[\p{Lu}]?[\p{Ll}\p{M}\’\']+")
inp_tag_text_src = inp_tag.get_text()
inp_tag_text = inp_tag_text_src
inp_tag_text = unicodedata.normalize("NFKD", inp_tag_text) # replace \xa0 (unbreaking space) with space
# preprocessing for converting references from text materials to the common format
inp_tag_text = bible_codes.ref_tag_preprocess(inp_tag_text)
inp_tag_text = inp_tag_text + ";"
#split references into list of references to add book names to references without book name
inp_tag_list = inp_tag_text.split(";")
# initializes variables
inp_tag_text = "" # text will be rewrote
book_name = "-"
for i_elem in range (0, len(inp_tag_list)): # enumerates elements in list of references
end_alpha_n = -1 # here will be stored end of part with letters is the end of book name
elem_not_empty = False # here will be stored mark of emptiness of element for skipping empty elements
for (i_lett) in range(0, len(inp_tag_list[i_elem])): # enumerates letters in reference
if (inp_tag_list[i_elem][i_lett].isalpha() or inp_tag_list[i_elem][i_lett] == "."): # is it part of book name?
end_alpha_n = i_lett # moves position of the end of book name
if (not(elem_not_empty) and (inp_tag_list[i_elem][i_lett].isalpha() or inp_tag_list[i_elem][i_lett].isdigit())):
elem_not_empty = True # marks element as not empty
if (elem_not_empty): # handles not empty element
if end_alpha_n > 0 : # here is book name
book_name = inp_tag_list[i_elem][0:end_alpha_n + 1] # remembers it for possible using for next reference
else: # here is no book name
inp_tag_list[i_elem] = book_name + inp_tag_list[i_elem] # add book name from previous reference with book name
inp_tag_text = inp_tag_text + ";" + inp_tag_list[i_elem] # add not empty element to the end of the string with references
# collect to refs all references to Bible texts
refs = find_refs.findall(inp_tag_text)
if (DEBUG_LEVEL > 0):
print("process doc {0},\n tag {1}, \n inp_tag_text {2}".format(doc, inp_tag, inp_tag_text))
print("find_refs {0}".format(refs))
print(" * references: *")
# insert ";" between references, do not insert in the end
is_last_ref = True #mark: it is the end reference, beginning from the end
book_name = ""
# process references from end to beginning
# to not to process result of processing
for ref in reversed(refs):
book_name_parse_ref = parse_ref.match(ref)
if (book_name_parse_ref == None):
#there is reference without book name, take the last found book name
ref = book_name + ref
book_name_parse_ref = parse_ref.match(ref)
book_name_parse_ref_group = book_name_parse_ref.group()
book_name = book_name_parse_ref_group.replace(" ", "")
book_name_as_list = list(book_name)
if (book_name_as_list[0].isdigit()):
book_name_as_list[1] = book_name_as_list[1].upper()
else:
book_name_as_list[0] = book_name_as_list[0].upper()
book_name_to_find = "".join(book_name_as_list)
book_N = bible_codes.book_index_to_MyBible.get(book_name_to_find)
if (book_N == None):
print("! referense not recognised, refs : {0} -> {1} ; ref {2}; book name {3}".format(inp_tag_text_src, refs, ref, book_name))
inp = ""
while (not(inp == 'y' or inp == 'n')):
print("exit? (y/n)")
inp = input()
if (inp == 'y'):
sys.exit()
if (DEBUG_LEVEL > 0):
print("ref: {0} parsed is {1} name is {2}, N is {3}".format(ref, parse_ref.match(ref), book_name, book_N))
numeric_part = (ref[parse_ref.match(ref).span()[1] + 1:]).replace(" ", "")
# if numeric part includes list of verses separated by commas like "Mt. 1:2-4, 5, 7, 9" then divide into list of references
numeric_part_list = numeric_part.split(",") # divides reference into parts
numeric_part_list = list(filter(None, numeric_part_list)) # removes empty strings from list
# convert consecutive number into range
to_continue = len(numeric_part_list) > 1 # initializes mark to continue by checking after current element there is one or more elements
ind = 0 # index of the current element
while(to_continue):
repeat_index = False # if found incorrect part then deletes it and repeat without it
if (numeric_part_list[ind]) == "": # checks if it is empty element
del numeric_part_list[ind] # deletes empty element
else:
if (numeric_part_list[ind].find(":") >= 0): # checks is it element with head number
verse_part = numeric_part_list[ind].split(":")[1] # if it is then remembers part with verses, it is after ":"
else:
verse_part = numeric_part_list[ind] # else all element is verses
try:
if (verse_part.find("-") >= 0): # checks element is range
range_end_number = int(verse_part.split("-")[1]) # if it is then remembers end of range, it will be used to concatenating with next verses
else:
range_end_number = int(verse_part) # if it is not range, then it is a single verse and it is the end of range itself
except ValueError:
print("incorrect verse found while handling {0}".format(inp_tag)) # print error message and diagnostic information
print("reference is {0}".format(ref))
print("numeric part is {0}; verse part is not integer: {1}".format(numeric_part_list[ind], verse_part))
del numeric_part_list[ind] # deletes incorrect element
repeat_index = True
if not(repeat_index):
if (ind < len(numeric_part_list) - 1): # current element is not the last element
if (numeric_part_list[ind + 1].find("-") >= 0 or numeric_part_list[ind + 1].find(":") >= 0): # next element is range or in new head, skip
pass
else:
try:
numeric_part_next_value = int(numeric_part_list[ind + 1])
except ValueError:
print("incorrect verse found while handling {0}".format(inp_tag)) # print error message and diagnostic information
print("reference is {0}".format(ref))
print("numeric part in next element branch is {0}".format(numeric_part_list[ind + 1]))
del numeric_part_list[ind + 1] # deletes incorrect element
repeat_index = True
if not(repeat_index):
if (numeric_part_next_value == (range_end_number + 1)):
if (numeric_part_list[ind].find("-") >= 0): # element is range
numeric_part_list[ind] = numeric_part_list[ind].split("-") + "-" + numeric_part_list[ind + 1]
else:
numeric_part_list[ind] = numeric_part_list[ind] + "-" + numeric_part_list[ind + 1]
del numeric_part_list[ind + 1]
if (ind < len(numeric_part_list) - 1): # checks if it is element after current element
if not(repeat_index): # if element deleted then keep current value of index, it points to the next element
ind = ind + 1 # increment index for handling the next element
else:
to_continue = False # if it is the last element then finishes
# and add head numbers to verses
if len(numeric_part_list) > 1: # if it is a reference with heads only then skip
head_number = ""
for i1, numeric_part_elem in enumerate(numeric_part_list) :
if (numeric_part_elem.find(":") >= 0): # element considers head number
head_number = numeric_part_elem.split(":")[0] # remembers head number
else :
numeric_part_list[i1] = head_number + ":" + numeric_part_elem # add head number if there is no head number
# concatenate reference from reference header "B:",
# book number and head number with verse number
for numeric_part_elem in reversed(numeric_part_list) :
if (book_N == 380 or book_N == 700 or book_N == 710 or book_N == 720 or book_N == 640):
# if book Obadiah or 2 John or 3 John or Jude or Philemon,
# which has one head then add to the reference head one "1:"
MyBible_ref = "B:{0} 1:{1}".format(book_N, numeric_part_elem)
else: