187 lines
5.5 KiB
Python

# -*- coding: utf-8 -*-
# Copyright 2023 Ant Group CO., Ltd.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
# in compliance with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License
# is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
# or implied.
import os
import re
import string
import sys
from configparser import ConfigParser
from pathlib import Path
from shutil import copy2, copystat
from stat import S_IWUSR as OWNER_WRITE_PERMISSION
from typing import Optional
import click
from tabulate import tabulate
from knext import rest
from knext.common.template import render_templatefile
TEMPLATES_TO_RENDER = (
(".knext.cfg.tmpl",),
("README.md.tmpl",),
("schema", "${project}.schema.tmpl"),
("reasoner", "demo.dsl.tmpl"),
("builder", "operator", "demo_extract_op.py"),
("builder", "job", "data", "Demo.csv"),
("builder", "job", "demo.py.tmpl"),
)
def list_project():
"""
List all project information.
"""
client = rest.ProjectApi()
projects = client.project_get()
table_data = []
for project in projects:
item = project.to_dict()
table_data.append(
[item["id"], item["name"], item["namespace"], item["description"]]
)
table_headers = ["ID", "Name", "Namespace", "Description"]
table = tabulate(table_data, table_headers, tablefmt="github")
click.echo(table)
def _recover_project(prj_path: str):
"""
Recover project by a project dir path.
"""
if not Path(prj_path).exists():
click.secho(f"ERROR: No such directory: {prj_path}", fg="bright_red")
sys.exit()
if prj_path not in sys.path:
sys.path.append(prj_path)
prj = Path(prj_path).resolve()
cfg_file = prj / ".knext.cfg"
cfg = ConfigParser()
cfg.read(cfg_file)
project_name = cfg.get("local", "project_name")
namespace = cfg.get("local", "namespace")
desc = cfg.get("local", "description")
client = rest.ProjectApi()
project_create_request = rest.ProjectCreateRequest(
name=project_name, desc=desc, namespace=namespace
)
project = client.project_create_post(project_create_request=project_create_request)
cfg.set("local", "project_id", str(project.id))
with open(cfg_file, "w") as config_file:
cfg.write(config_file)
click.secho(
f"Project [{project_name}] with namespace [{namespace}] was successfully created from [{prj_path}].",
fg="bright_green",
)
@click.option("--name", help="Name of project.")
@click.option("--namespace", help="Prefix of project schema.")
@click.option("--desc", help="Description of project.")
@click.option(
"--prj_path",
help="If set, project will be created according to config file of this path.",
)
def create_project(
name: str, namespace: str, desc: Optional[str], prj_path: Optional[str]
):
"""
Create new project with a demo case.
"""
if prj_path:
_recover_project(prj_path)
sys.exit()
if not name:
click.secho("ERROR: Option [--name] is required.")
sys.exit()
if not namespace:
click.secho("ERROR: Option [--namespace] is required.")
sys.exit()
if not re.match(r"^[A-Z][A-Za-z0-9]{0,15}$", namespace):
raise click.BadParameter(
f"Invalid namespace: {namespace}."
f" Must start with an uppercase letter, only contain letters and numbers, and have a maximum length of 16."
)
project_dir = Path(namespace.lower())
if project_dir.exists():
raise click.ClickException(
f"Project directory [{namespace.lower()}] already exists."
)
client = rest.ProjectApi()
project_create_request = rest.ProjectCreateRequest(
name=name, desc=desc, namespace=namespace
)
project = client.project_create_post(project_create_request=project_create_request)
import knext
templates_dir = Path(knext.__path__[0]) / "templates/project"
_copytree(Path(templates_dir), project_dir.resolve(), namespace.lower())
for paths in TEMPLATES_TO_RENDER:
tplfile = Path(
project_dir,
*(string.Template(s).substitute(project=namespace.lower()) for s in paths),
)
render_templatefile(
tplfile,
project_name=name,
namespace=namespace,
project_dir=namespace.lower(),
project_id=project.id,
description=desc,
helper=f"{namespace.lower()}_schema_helper",
)
click.secho(
f"Project [{name}] with namespace [{namespace}] was successfully created in {project_dir.resolve()} \n"
+ "You can checkout your project with: \n"
+ f" cd {project_dir}",
fg="bright_green",
)
def _copytree(src: Path, dst: Path, project_name: str):
names = [x.name for x in src.iterdir()]
if not dst.exists():
dst.mkdir(parents=True)
for name in names:
_name = name.replace("${project}", project_name)
src_name = src / name
dst_name = dst / _name
if src_name.is_dir():
_copytree(src_name, dst_name, project_name)
else:
copy2(src_name, dst_name)
_make_writable(dst_name)
copystat(src, dst)
_make_writable(dst)
def _make_writable(path):
current_permissions = os.stat(path).st_mode
os.chmod(path, current_permissions | OWNER_WRITE_PERMISSION)