Completed
Push — master ( 8a3295...7d9c05 )
by Mohammad Mahdi
57s
created

version_control()   B

Complexity

Conditions 4

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 9
Bugs 5 Features 3
Metric Value
cc 4
c 9
b 5
f 3
dl 0
loc 24
rs 8.6845
1
import shutil  # Library For Work With File In High Level Like Copy
2
import webbrowser
3
from params import *
4
import socket
5
import requests
6
import re
7
import time
8
import sys
9
import urllib.request
10
import platform
11
import random
12
13
meta_input = ""
14
15
16
def convert_bytes(num):
17
    """ convert num to idiomatic byte unit
18
19
    :param num: the input number.
20
    """
21
    for x in ['bytes', 'KB', 'MB', 'GB', 'TB']:
22
        if num < 1024.0:
23
            return "%3.1f %s" % (num, x)
24
        num /= 1024.0
25
26
27
def file_size():
28
    """ Print the size of output file
29
30
    """
31
    list_of_files = os.listdir(out_dir)
32
    response = 0
33
    for file in list_of_files:
34
        file_info = os.stat(os.path.join(out_dir, file))
35
        response += file_info.st_size
36
    print_line(70, "*")
37
    print("Used Space --> " + convert_bytes(response))
38
    print_line(70, "*")
39
40
41
def download_badge(address):
42
    """ Download badge for website
43
44
    :param address: the address that should get badge
45
    """
46
    r = requests.get(address, stream=True)
47
    with open(os.path.join(image_dir, "badge.svg"), 'wb') as f:
48
        shutil.copyfileobj(r.raw, f)
49
50
51
def random_badge_color():
52
    """return a random color for badge
53
54
    """
55
    random_index = random.randint(0, len(badge_color_list) - 1)
56
    return badge_color_list[random_index]
57
58
59
def system_details():
60
    """ Show detail of system that code is runnig on
61
62
    """
63
    return platform.node() + " , " + platform.processor() + " ,  " + platform.platform()
64
65
66
def generation_time(time_1=None):
67
    """ Calculate the generation time
68
69
    :param time_1: time that passed but not counted in generation time
70
    :return :the amount of time that passed .
71
    """
72
    if time_1 is None:
73
        return time.perf_counter()
74
    else:
75
        return time.perf_counter() - time_1
76
77
78
def find_global_ip():
79
    """ Find the ip for use in API
80
81
    :return: return the IP.
82
    """
83
    try:
84
        response = requests.get(ip_finder_api)
85
        return response.text[:-1]
86
    except:
87
        return "0.0.0.0"
88
89
90
def create_badge(subject="qpage", status=version, color="blue", random=False):
91
    if random:
92
        color = random_badge_color()
93
    else:
94
        if color not in badge_color_list:
95
            color = "orange"
96
    badge_adr = adv_badge_static + subject + "-" + status + "-" + color + '.svg'
97
    if internet():
98
        download_badge(badge_adr)
99
        return os.path.join(image_dir, "badge.svg")
100
    else:
101
        return badge_adr
102
103
104
def is_sample_downloaded():
105
    """ Check the sample site material is downloaded
106
107
    :return : list of the materials
108
    """
109
    download_list = []
110
    if "profile.png" not in os.listdir(image_dir):
111
        download_list.append(0)
112
    if "font.TTF" not in os.listdir(font_dir):
113
        download_list.append(1)
114
    if "resume.pdf" not in os.listdir(doc_dir) and "resume.txt" not in os.listdir(doc_dir):
115
        download_list.extend([2, 3])
116
    if "icon.ico" not in os.listdir(image_dir):
117
        download_list.append(4)
118
    return download_list
119
120
121
def download_lorem():
122
    """ Download the lorem file
123
124
    """
125
    if internet():
126
        urllib.request.urlretrieve("http://www.qpage.ir/sample/Latin-Lipsum.txt", "Latin-Lipsum.txt")
127
    else:
128
        print("Error In Download Lorem")
129
130
131
def read_lorem(char=100):
132
    """ find and read lorem
133
134
    :param char: the amount of char that needed
135
    :return : the lorme string
136
    """
137
    try:
138
        if "Latin-Lipsum.txt" not in os.listdir(work_dir):
139
            download_lorem()
140
        lorem_file = open("Latin-Lipsum.txt", "r")
141
        lorem_text = lorem_file.read()
142
        lorem_file.close()
143
        return " ".join(lorem_text.split(" ")[:char])
144
    except:
145
        return None
146
147
148
def sample_site_download(item_list):
149
    """Download sample material for make a fake site
150
151
    :param item_list: Download items form item_list
152
    """
153
    try:
154
        if internet():
155
            for i in item_list:
156
                print("Downloading " + sample_dict_message[i] + " . . . [" + str(i + 1) + "/5]")
157
                print_line(70)
158
                urllib.request.urlretrieve(list(sample_dict_addr.values())[i],
159
                                           os.path.join(image_dir, list(sample_dict_addr.keys())[i]))
160
            print("Done! All Material Downloaded")
161
            print_line(70)
162
        else:
163
            print("Error In Internet Connection!")
164
            print_line(70)
165
    except:
166
        print("Error in downloading sample files check your internet conection")
167
        print_line(70)
168
169
170
def logger(status=False, perf_time=None):
171
    """Show the log of the app
172
173
    :param status: show status of app.
174
    :param perf_time : show the time passed for generate files
175
    """
176
    file = open("build_log.txt", "a")
177
    if not status:
178
        file.write("Failed  " + str(datetime.datetime.now()) + "\n")
179
    else:
180
        file.write("Success " + str(datetime.datetime.now()) + "\n")
181
        file.write("Generation Time: " + str(perf_time) + "\n")
182
    file.close()
183
184
185
def print_line(number, char="-"):
186
    """ Print a Line
187
188
    :param number: the amount char that in lien
189
    :param char  : the char that used to draw line
190
    """
191
    line = ""
192
    for i in range(number):
193
        line += char
194
    print(line)
195
196
197
def name_standard(name):
198
    """ return the Standard version of the input word
199
200
    :param name: the name that should be standard
201
    :return name: the standard form of word
202
    """
203
    reponse_name = name[0].upper() + name[1:].lower()
204
    return reponse_name
205
206
207
def address_print():
208
    """Print the working directory
209
210
    """
211
    print_line(70, "*")
212
    print("Where --> " + work_dir)
213
    print_line(70, "*")
214
215
216
def create_folder():
217
    """This Function Create Empty Folder At Begin
218
219
    """
220
    folder_flag = 0
221
    list_of_folders = os.listdir(work_dir)
222
    for i in ["doc", "image", "output", "font"]:
223
        if i not in list_of_folders:
224
            os.mkdir(i)
225
            folder_flag += 1
226
            if i == "doc":
227
                file = open(os.path.join(doc_dir, "index.txt"), "w")
228
                if read_lorem() is None:
229
                    file.write("This is For First Page . . .")
230
                else:
231
                    file.write(read_lorem())
232
                file.close()
233
    return bool(folder_flag)
234
235
236
def page_name_update():
237
    """This Function Update Page Names
238
239
    """
240
    for i in os.listdir(doc_dir):
241
        if i.find(".txt") != -1 and i[:-4].upper() != "INDEX":
242
            actual_name.append(i[:-4])
243
            page_name.append(i[:-4])
244
245
246
def menu_maker():
247
    """Top Menu Maker In each html page
248
249
    """
250
    result = "<center>"
251
    for i in range(len(page_name)):
252
        if page_name[i] == "Home":
253
            targets_blank = ""
254
        else:
255
            targets_blank = 'target="blank"'
256
            # Hyper Link To Each Page In HTML File
257
            result += '\t<a href="' \
258
                      + actual_name[i] + '.html"' + targets_blank + '>' + name_standard(page_name[i]) + "</a>\n"
259
        result += "&nbsp\n"
260
    result += "</center>"
261
    result = result + "\t\t" + break_line  # Add Break line to End Of The Menu
262
    return result  # Return All Of The Menu
263
264
265
def menu_writer():  #
266
    """Write menu_maker output in html and close file after
267
268
    """
269
    message = menu_maker()
270
    for i in range(len(page_name)):
271
        file = open(os.path.join(out_dir, actual_name[i] + ".html"), "a")
272
        file.write(message)
273
        file.close()
274
275
276
def print_meta():
277
    """Add meta to html files
278
279
    :return static_meta: The meta that created
280
    """
281
    global meta_input
282
    meta_input = input("Please Enter Your Name : ")
283
    static_meta = '<meta name="description" content="Welcome to homepage of ' + meta_input + '"/>\n'
284
    static_meta += '<meta property="og:title" content="' + meta_input + '"/>\n'
285
    static_meta += '<meta property="og:site_name" content="' + meta_input + '"/>\n'
286
    static_meta += '<meta property="og:image" content="favicon.ico" />\n'
287
    if len(meta_input) < 4:
288
        warnings.append("[Warning] Your input for name is too short!!")
289
    return static_meta
290
291
292
def html_init(name):
293
    """Create Initial Form Of each Html Page Like Title And HTML  And Body Tag.
294
295
    :param name: the name of html file.
296
    """
297
298
    html_name = os.path.join(out_dir, name + ".html")
299
    file = open(html_name, "w")
300
    file.write("<html>\n")
301
    file.write("\t<head>\n")
302
    if name == "index":
303
        file.write("\t\t<title>Welcome To My Homepage</title>\n")
304
    else:
305
        file.write("\t\t<title>" + name_standard(name) + "</title>\n")
306
    file.write('<link rel="stylesheet" href="styles.css" type="text/css"/>\n')
307
    css_link = 'https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'
308
    file.write('<link rel="stylesheet" href= ' + css_link + ' type="text/style"/>\n')
309
310
    if name == 'index':  # Add meta only for index page
311
        file.write(print_meta())
312
313
    file.write("\t</head>\n")
314
    file.write('\t<body class="body_tag">\n')
315
    file.close()
316
317
318
def html_end(name):
319
    """Create End Of The Html and close file
320
321
    :param name: The name of html file.
322
    """
323
    html_name = os.path.join(out_dir, name + ".html")
324
    file = open(html_name, "a")
325
    file.write("\t</body>\n")
326
    file.write("</html>")
327
    file.close()
328
329
330
def close_files():
331
    """Close all the files.
332
333
    """
334
    for i in files:
335
        i.close()
336
337
338
def LSM_translate(line, center):
339
    # TODO : write a document for this function
340
    """ ????
341
342
    :param line: the input line.
343
    :param center: put it in center
344
345
    :return : return a list contain text header end and header begin
346
    """
347
    line.strip()
348
    text = line
349
    header_start = '<h4 class="color_tag">'
350
    header_end = "</h4>"
351
    if line.find("[L]") != -1:
352
        header_start = '<h2 class="color_tag">'
353
        header_end = "</h2>"
354
        text = line[3:]
355
    elif line.find("[S]") != -1:
356
        header_start = '<h5 class="color_tag">'
357
        header_end = "</h5>"
358
        text = line[3:]
359
    elif line.find("[M]") != -1:
360
        text = line[3:]
361
    if center:  # Centuries Text If Condition Is True For Manual Centering
362
        header_start = "<center>" + header_start
363
        header_end += "</center>"
364
    if text.find("[center]") != -1:  # Find Center Tag In Each Line
365
        header_start = "<center>" + header_start
366
        header_end += "</center>"
367
        text = text[:text.find("[center]")]
368
    return [text, header_end, header_start]
369
370
371
def print_text(text_file, file, center=False, close=False):  # Write Text Part Of Each Page
372
    """Write the text part of each page
373
374
    :param text_file: Text tha should be written.
375
    :param file     : The file that text will be written inside.
376
    :param center   : put the text in center.
377
    :param close    : close file after done editing
378
379
    :type close : bool
380
    :type center: bool
381
382
    """
383
384
    text_code = ""
385
    for line in text_file:
386
        if len(line) == 1:
387
            text_code = space
388
        else:
389
            text_header = LSM_translate(line, center)
390
            text = text_header[0]
391
            header_end = text_header[1]
392
            header_start = text_header[2]
393
            text_code = header_start + text + header_end + "\n"
394
        file.write(text_code)
395
    if close:
396
        file.close()
397
398
399
def print_image(file, image_format="jpg", close=False):
400
    """Write Image Part OF The Page.
401
402
    :param file: The file that images will be added.
403
    :param close : close file after done editing.
404
    :param image_format: the format of image
405
406
    :type close : bool
407
    """
408
    for i in range(len(size_box)):
409
        print(i, "-", size_box[i])
410
    image_size = int(input("Please Enter Profile Image Size : "))  # Choose Profile Image Size
411
    image_size_string = size_box[2]  # Getting Html String From size_box list default mode (Medium)
412
    if 0 <= image_size < len(size_box):
413
        image_size_string = size_box[image_size]
414
    image_code = '<center><img src="image.' + image_format + '"' + ', width=' + image_size_string + ' alt="profile image"></img></center>\n'
415
    file.write(image_code)
416
    if close:
417
        file.close()
418
419
420
def print_download(file, name, link, center=False, close=False):
421
    """ Create Download Link in page
422
423
    :param file: The file that contain html of page.
424
    :param name: The name of the link
425
    :param link: The place that name is Linked
426
    :param center: put the text in center
427
    :param close : close file after done editing
428
429
    :type center: bool
430
    :type close : bool
431
432
    """
433
    link_code = "<a href=" + '"' + link + '"' + target_blank + '>' + name + "</a>"
434
    if center:
435
        link_code = "<center>" + link_code + "</center>"
436
    file.write(link_code + "\n")
437
    file.write(break_line)
438
    if close:
439
        file.close()
440
441
442
def print_adv(file, close=True):
443
    """ Print the advertisement.
444
445
    :param file  : The file that should ad to it.
446
    :param close : Close file after add ad
447
    """
448
    file.write(break_line)
449
    file.write(
450
        '<center>' + "<p>" + "Generated " + today_time + " By" + "</p>" + '<a href=' + '"' + homepage + '"' + target_blank + '>' + '<img src="' + create_badge(
451
            random=True) + '"alt="Qpage">' + '</a> </center>')
452
    if close:
453
        file.close()
454
455
456
def build_index(file):
457
    """ Find and build index page
458
459
    :param file: The index file.
460
    """
461
    image_name = ""
462
    img_format = "jpg"
463
    file_of_images = os.listdir(image_dir)
464
    for i in range(len(file_of_images)):
465
        for form in imformat_box:
466
            if file_of_images[i].find("." + form) != -1:
467
                image_name = os.path.join(image_dir, file_of_images[i])
468
                img_format = form
469
                global image_counter
470
                image_counter = 1
471
                break
472
    shutil.copyfile(image_name, os.path.join(out_dir, "image." + img_format))
473
    print_image(file, img_format)
474
475
476
def build_resume(file):
477
    """ Find and build resume page.
478
479
    :param file: The resume file.
480
    """
481
    resume_name = ""
482
    file_of_docs = os.listdir(doc_dir)
483
    for i in range(len(file_of_docs)):
484
        if file_of_docs[i].find(".pdf") != -1:
485
            resume_name = os.path.join(doc_dir, file_of_docs[i])
486
            global pdf_counter
487
            pdf_counter = 1
488
            break
489
    shutil.copyfile(resume_name, os.path.join(out_dir, "Resume.pdf"))
490
    print_download(file, "Download Full Version", "Resume.pdf", center=True)
491
492
493
def contain(name):
494
    """main function That Open Each Page HTML File and call other function to write data in it
495
496
    :param name: the name of the file that should be written
497
    """
498
    #
499
    file = open(os.path.join(out_dir, name + ".html"), "a")
500
    text_file = open(os.path.join(doc_dir, name + ".txt"), "r")
501
    files.append(file)
502
    files.append(text_file)
503
504
    if name.upper() == "INDEX":
505
        build_index(file)
506
    elif name.upper() == "RESUME":
507
        build_resume(file)
508
509
    print_text(text_file, file)
510
    print_adv(file)
511
512
513
def clear_folder(path):
514
    """This Function Get Path Of Foldr And Delete Its Contains
515
516
    :param path: the path that gonna be deleted.
517
    """
518
519
    if os.path.exists(path):
520
        list_of_files = os.listdir(path)
521
        for file in list_of_files:
522
            os.remove(os.path.join(path, file))
523
    else:
524
        os.mkdir(path)
525
526
527
def print_warning():
528
    """ Print Warinigns!
529
530
    """
531
    print(str(len(warnings)) + " Warning , 0 Error")
532
    for i in range(len(warnings)):
533
        print(str(i + 1) + "-" + warnings[i])
534
535
536
def get_color_code():
537
    """Ask for selecting color of text and background
538
539
    :return list: background and text color
540
    """
541
    for i in range(len(color_box)):
542
        print(i, "-", color_box[i])
543
    back_color_code = int(input("Please enter your background color : "))
544
    if back_color_code not in range(7):
545
        back_color_code = 0
546
    text_color_code = int(input("Please enter your text color : "))
547
    if text_color_code not in range(7):
548
        text_color_code = 1
549
    return [back_color_code, text_color_code]
550
551
552
def color_code_map():
553
    """ Check and insert colors that is chosen.
554
555
    :return list: background and text color
556
    """
557
    [back_color_code, text_color_code] = get_color_code()
558
    if text_color_code == back_color_code:
559
        warnings.append(warning_dict["color_warning"] + " Your text color and background color are same!!")
560
    background_color = color_box[back_color_code]  # convert code to color string in color_box
561
    text_color = color_box[text_color_code]  # convert code to color string in color_box
562
    return [background_color, text_color]
563
564
565
def css_font(font_folder):
566
    """ Search and file all fonts.
567
568
    :param font_folder: the folder to search.
569
    :return list : font_flag and the current format
570
    """
571
    font_flag = 0  # 0 If there is no font file in font_folder
572
    current_font_format = None
573
    for i in font_folder:
574
        for j in range(len(font_format)):  # search for other font format in font box
575
            if i.lower().find(font_format[j]) != -1:  # If there is a font in font folder
576
                shutil.copyfile(os.path.join(font_dir, i),
577
                                os.path.join(out_dir, "qpage" + font_format[j]))  # copy font file to output folder
578
                font_flag = 1  # Turn Flag On
579
                current_font_format = font_format[j]  # font format of current selected font for css editing
580
    return [font_flag, current_font_format]
581
582
583
def font_creator(css_file, font_section):
584
    """ Ask and Select font.
585
586
    :param css_file: the file that font css will be added to.
587
    :param font_section: the font section of css file
588
589
    :return font_section: the font section of css after edit
590
    """
591
    font_folder = os.listdir(font_dir)
592
    details = css_font(font_folder)
593
    current_font_format = details[1]
594
    font_flag = details[0]
595
596
    if font_flag == 1:  # check flag if it is 1
597
        css_file.write(
598
            "@font-face{\nfont-family:qpagefont;\nsrc:url(qpage"
599
            + current_font_format
600
            + ");\n}\n")  # Write font-face in html
601
602
        font_section = "font-family:qpagefont;\n"  # Update Font Section For Body Tag
603
        for i in range(len(fontstyle_box)):
604
            print(i, "-", fontstyle_box[i])
605
        font_style = int(input(" Please choose your font style "))
606
        if font_style < len(fontstyle_box):
607
            font_style = fontstyle_box[font_style]
608
        else:
609
            font_style = "normal"
610
        font_section = font_section + "font-style:" + font_style + ";\n"
611
    else:
612
        warnings.append(warning_dict["font_warning"] + " There is no specific font set for this website!!")
613
    return font_section
614
615
616
def css_creator():
617
    """Ask For background and text color in and make css """
618
    font_section = 'font-family : Georgia , serif;\n'
619
    colors = color_code_map()
620
    background_color = colors[0]
621
    text_color = colors[1]
622
623
    css_file = open(os.path.join(out_dir, "styles.css"), "w")  # open css file
624
    font_section = font_creator(css_file, font_section)
625
626
    css_file.write(
627
        ".body_tag{\n"
628
        + "background-color:"
629
        + background_color
630
        + ";\n"
631
        + font_section
632
        + css_margin
633
        + css_animation_1
634
        + "}\n")  # write body tag
635
636
    css_file.write(".color_tag{\n" + "color:" + text_color + ";\n}")  # write color_tag in css
637
    css_file.write(css_animation_2)
638
    css_file.close()  # close css file
639
640
641
def preview():
642
    """Preview website in browser """
643
    # TODO: not working on unix
644
645
    webbrowser.open(os.path.join(out_dir, "index.html"))
646
647
648
def error_finder():
649
    """ Check and find error that display it"""
650
    error_vector = []
651
    pass_vector = []
652
    pdf_counter = 0
653
    image_list = os.listdir(image_dir)
654
    doc_list = os.listdir(doc_dir)
655
    if image_counter == 1:
656
        pass_vector.append("[Pass] Your profile image in OK!!")
657
    else:
658
        error_vector.append(error_dict["image_error"] + " Your profile image is not in correct format")
659
    if len(doc_list) == 0:
660
        error_vector.append(error_dict["empty_error"] + "There is no file in doc folder ( index.txt and .pdf file in "
661
                                                        "necessary)")
662
    else:
663
        if "index.txt" in doc_list:
664
            pass_vector.append("[Pass] index.txt file OK!")
665
        else:
666
            error_vector.append(error_dict["firstpage_error"] + " index.txt is not in doc folder!")
667
        if pdf_counter == 0:
668
            error_vector.append(error_dict["resume_error"] + "[Error] Where Is Your Resume File? It should be in doc "
669
                                                             "folder")
670
        else:
671
            pass_vector.append("[Pass] Your Resume File is OK!!")
672
    return [error_vector, pass_vector]
673
674
675
def icon_creator():
676
    """ Find .ico file and use it as favicon of website."""
677
    icon_flag = 0
678
    for file in os.listdir(image_dir):
679
        if file.endswith('ico'):
680
            shutil.copy(os.path.join(image_dir, file), out_dir)
681
            os.rename(os.path.join(out_dir, file), os.path.join(out_dir, 'favicon.ico'))
682
            icon_flag = 1
683
            break
684
    if "favicon.ico" in os.listdir(work_dir) and icon_flag == 0:
685
        shutil.copy(os.path.join(work_dir, "favicon.ico"), out_dir)
686
        warnings.append(warning_dict["icon_warning"] + " There is no icon for this website")
687
688
689
def robot_maker():
690
    """ Create Robots.txt for pages """
691
    robots = open(os.path.join(out_dir, "robots.txt"), "w")
692
    robots.write("User-agent: *\n")
693
    robots.write("Disallow: ")
694
    robots.close()
695
696
697
def internet(host="8.8.8.8", port=53, timeout=3):
698
    """ Check Internet Connections.
699
700
    :param  host: the host that check connection to
701
    :param  port: port that check connection with
702
    :param  timeout: times that check the connnection
703
704
    :return bool: True if Connection is Stable
705
    """
706
    try:
707
        socket.setdefaulttimeout(timeout)
708
        socket.socket(socket.AF_INET, socket.SOCK_STREAM).connect((host, port))
709
        return True
710
    except Exception as ex:
711
        return False
712
713
714
def server():
715
    """Get Server response."""
716
    global meta_input
717
    headers = {'content-type': 'application/json', "NAME": meta_input, "Version": version, "SYSTEM": system_details(),
718
               "IP": find_global_ip()}
719
    response = requests.get(server_api, headers=headers)
720
    # print(response)
721
    # TODO : use the server response
722
723
724
def version_control():
725
    """ Check and update Versions. """
726
727
    try:
728
        # print("Check for new version . . .")
729
        # print_line(70)
730
        version_pattern = r"last_version:(.+)"
731
        if internet():
732
            response = requests.get("http://www.qpage.ir/releases.html")
733
            body = response.text
734
            last_version = float(re.findall(version_pattern, body)[0][:-3])
735
            if last_version > float(version):
736
                print_line(70)
737
                print("**New Version Of Qpage Is Available Now (Version " + str(last_version) + ")**")
738
                print("Download Link -->" + "https://github.com/sepandhaghighi/qpage/archive/v" + str(
739
                    last_version) + ".zip")
740
                print_line(70)
741
            else:
742
                # TODO : fix version control else
743
                pass
744
                # print("Already Updated!!!")
745
                # print_line(70)
746
    except:
747
        pass
748
749
750
def enter_to_exit():
751
    """Quit Project by pressing a key.
752
753
    """
754
755
    print_line(70, "*")
756
    response = input("Enter [R] for restart Qpage and any other key to exit : ")
757
    if response.upper() != "R":
758
        sys.exit()
759
760
761
def wait_func(iteration):
762
    """Wait for-in range Iteration.
763
764
    :param iteration: the amount of wait.
765
    """
766
767
    for _ in range(iteration):
768
        time.sleep(1)
769
        print(".")
770