<<<<<<< HEAD
from fs.copy import copy_fs
from jinja2 import Environment, FileSystemLoader
import json
import os
import re
=======
from fs.copy import copy_fs, copy_file
from jinja2 import Environment, FileSystemLoader
import fs
import json
import os
import re
import hashlib
import time
import shutil
>>>>>>> 735d39eb4d0c3134b62bf4fe1b7a2ca0ea8da1ca

'''
流程：
一、将resources里面所有文件夹拷贝到当前目录下
二、解析json配置文件，遍历每一项生成/model/view/controller
三、自动运行项目
'''

<<<<<<< HEAD
# 将字符串首字母转换成大写字母
def convertFirstLetterUpper(text_str):
    # return text_str.capitalize()
    # print("////////////////////>>>>", text_str)
    return re.sub("([a-zA-Z])", lambda x: x.groups()[0].upper(), text_str, 1)

# ROOT = os.path.abspath(os.getcwd())
jinja_env = Environment(loader=FileSystemLoader(os.path.join(os.getcwd(), 'templates')))
jinja_env.filters['letterUpper'] = convertFirstLetterUpper
input_dir = None
output_dir = None

def copyFiles(src_dir, dst_dir):
    copy_fs(src_dir, dst_dir)

def handleResources(config):
    # 处理路由页面
    # 遍历config文件，收集所有的action和name，action和name的首字母必须大写
    # 然后才生成路由配置页面
    target_file = os.sep.join(["views", "__init__.py"])
    handleRender(config, 'router.tpl', target_file)

def handleSignal(config):
    # 生成信号槽模块
    target_file = os.sep.join(["application", "signal_manager.py"])
    handleRender(config, 'signal_manager.tpl', target_file)

    target_file = os.sep.join(["controllers", "__init__.py"])
    handleRender(config, 'signal_manager_init.tpl', target_file)

=======
# 比较两个文件内容是否相同，这里没有使用md5内容摘要算法，MD5算法的缺点是，针对大文件，计算耗时。虽然咱们这里不存在大文件，但是条条大路通罗马嘛，试试其他方法也挺好。
def cmp_file(file1, file2):
    f1 = os.stat(file1)
    f2 = os.stat(file2)

    if f1.st_size != f2.st_size:
        return False

    buf_size = 8 * 1024
    with open(file1, "rb") as fp1, open(file2, "rb") as fp2:
        while True:
            buf1 = fp1.read(buf_size)
            buf2 = fp2.read(buf_size)

            if buf1 != buf2:
                return False

            # 这里表示文件读完了
            if not buf1 or not buf2:
                return True


def cmp_md5(contnet1, content2):
    m1 = hashlib.md5()
    m1.update(bytearray(contnet1, 'utf-8'))

    m2 = hashlib.md5()
    m2.update(bytearray(content2, 'utf-8'))

    return m1.hexdigest() == m2.hexdigest()

# 将字符串首字母转换成大写字母
def convertFirstLetterUpper(text_str):
    # return text_str.capitalize()
    # print("////////////////////>>>>", text_str)
    return re.sub("([a-zA-Z])", lambda x: x.groups()[0].upper(), text_str, 1)

# ROOT = os.path.abspath(os.getcwd())
jinja_env = Environment(loader=FileSystemLoader(os.path.join(os.getcwd(), 'templates')))
jinja_env.filters['letterUpper'] = convertFirstLetterUpper
input_dir = None
output_dir = None

def copyFiles(src_dir, dst_dir):
    copy_fs(src_dir, dst_dir)

def handleResources(config):
    # 处理路由页面
    # 遍历config文件，收集所有的action和name，action和name的首字母必须大写
    # 然后才生成路由配置页面
    target_file = os.sep.join(["views", "__init__.py"])
    handleRender(config, 'router.tpl', target_file)

def handleSignal(config):
    # 生成信号槽模块
    target_file = os.sep.join(["application", "signal_manager.py"])
    handleRender(config, 'signal_manager.tpl', target_file)

    target_file = os.sep.join(["controllers", "__init__.py"])
    handleRender(config, 'signal_manager_init.tpl', target_file)

>>>>>>> 735d39eb4d0c3134b62bf4fe1b7a2ca0ea8da1ca
def handleModel(config, application):
    # 将所有有默认值的字段分为一组，没有默认值的字段分为另一组
    # 生成模板代码时，无默认值的字段在前，有默认值的字段字在后
    # 收集表字段信息
    fields = []
    extend = False
    for m in config.get("model").get("fields"):
        fields.append(m.get("name"))
        extend = True
        print(m)
<<<<<<< HEAD

    target_file = os.sep.join(["models", "{}.py".format(config.get("name"))])
    handleRender(config, 'model.tpl', target_file, fields=fields, extend=extend, application=application)

=======

    target_file = os.sep.join(["models", "{}.py".format(config.get("name"))])
    handleRender(config, 'model.tpl', target_file, fields=fields, extend=extend, application=application)
    # 多次复制时，会报文件已存在错误，因此要先删除
    target_file = os.sep.join(["models", "base.py"])
    if os.path.exists(target_file):
        os.remove(target_file)
    handleRender(config, 'base.tpl', target_file, application=application)

>>>>>>> 735d39eb4d0c3134b62bf4fe1b7a2ca0ea8da1ca
def handleView(config):
    target_file = os.sep.join(["views", "{}.py".format(config.get("name"))])
    handleRender(config, 'view.tpl', target_file)

def handleController(config):
    # 根据模型字段自动从models列表中取出file name信息和class name信息
    target_file = os.sep.join(["controllers", "{}.py".format(config.get("name"))])
    handleRender(config, 'controller.tpl', target_file)

def handleRender(result, tpl, target_file, **kwargs):
    global output_dir
    # print("=========>", result.get("name"), "{}.py".format(result.get("name")))
    jinja_tpl = jinja_env.get_template(tpl)
    content = jinja_tpl.render({ "config": result, **kwargs })
    # print("############", output_dir)
<<<<<<< HEAD
=======

>>>>>>> 735d39eb4d0c3134b62bf4fe1b7a2ca0ea8da1ca
    target_file = os.sep.join([output_dir, target_file])
    if not os.path.exists(os.path.dirname(target_file)):
        os.makedirs(os.path.dirname(target_file))
    
<<<<<<< HEAD
    with open(target_file, 'w', encoding='utf-8') as f:
        f.write(content)

=======
    # 这里需要比较目标文件夹是否已经存在同名文件，如果存在，则需要比较两个文件内容是否一致
    # 如果不一致时，则需要给旧的文件打上时间戳，作为备份文件

    # 具体流程：
    # 先将原来文件重命名，然后新的内容写入到重命名的文件中
    # 最后比较两个文件内容是否一致，如果一致，则删除重命名的那个旧的备份文件

    tmp_file = ""
    if os.path.exists(target_file) and os.stat(target_file).st_size > 0:
        n, e = os.path.splitext(target_file)
        tmp_file = "{}.{}{}".format(n, time.strftime("%Y%m%d%H%M%S", time.localtime()), e)
        if os.path.exists(tmp_file):
            os.remove(tmp_file)
        os.rename(target_file, tmp_file)

    with open(target_file, 'w', encoding='utf-8') as f:
        f.write(content)

    if cmp_file(tmp_file, target_file) == True:
        os.remove(tmp_file)

>>>>>>> 735d39eb4d0c3134b62bf4fe1b7a2ca0ea8da1ca
def parseConfig(config):
    # 解析配置文件
    for cfg in config.get("apis"):
        handleModel(cfg, config.get("application"))
        handleView(cfg)
        handleController(cfg)
    handleResources(config.get("apis"))
    handleSignal(config.get("apis"))

def readConfig():
    result = {}
    with open("config.json", "r+") as f:
        result = json.loads(f.read())
    return result

<<<<<<< HEAD
=======
# 备份数据库，判断目标文件夹下是否有.db文件，有的话，先备份到临时目录，待文件复制完成后，再放回原来位置
def backup_database():
    global output_dir
    target_dir = os.sep.join([os.path.dirname(output_dir), "tmp"])
    if not os.path.exists(target_dir):
        os.makedirs(target_dir)
    home_fs = fs.open_fs(output_dir)
    for file in home_fs.glob('*.db', namespaces=['details']):
        # print(file.path, os.path.normpath(os.sep.join([output_dir, file.path])))
        # copyFiles(os.path.normpath(os.sep.join([output_dir, file.path])), target_dir)
        shutil.copy(os.path.normpath(os.sep.join([output_dir, file.path])), target_dir)

# 恢复数据库，代码生成完成后，需要把之前复制的数据库文件，恢复到原来位置
def restore_database():
    global output_dir
    target_dir = os.sep.join([os.path.dirname(output_dir), "tmp"])

    if not os.path.exists(target_dir):
        return

    home_fs = fs.open_fs(target_dir)
    for file in home_fs.glob('*.db', namespaces=['details']):
        # copyFiles(os.path.normpath(os.sep.join([target_dir, file.path])), output_dir)
        shutil.copy(os.path.normpath(os.sep.join([target_dir, file.path])), output_dir)

    home_fs = fs.open_fs(target_dir)
    home_fs.removetree("/")
    if home_fs.exists("/"):
        os.removedirs(target_dir)

>>>>>>> 735d39eb4d0c3134b62bf4fe1b7a2ca0ea8da1ca
def run():
    global input_dir
    global output_dir
    input_dir = os.sep.join([os.getcwd(), "resources"])
    output_dir = os.sep.join([os.getcwd(), "build_out"])
<<<<<<< HEAD
    copyFiles(input_dir, output_dir)
    config = readConfig()
    # print(config)
    parseConfig(config)
=======
    backup_database()
    copyFiles(input_dir, output_dir)
    config = readConfig()
    parseConfig(config)
    restore_database()
>>>>>>> 735d39eb4d0c3134b62bf4fe1b7a2ca0ea8da1ca
    print("success ......")

if __name__ == "__main__":
    run()