def main(): client = cgtwq.DesktopClient() client.connect() win_unicode_console.enable() logging.basicConfig( level="INFO", format="%(levelname)-7s:%(name)s: %(message)s", ) print("{:-^50}".format("Link 导入 v{}".format(__version__))) print( """\ 所需表格格式: | 镜头 | 资产1 | 资产2 | | --------------------- | ------ | ------- | | SDKTEST_EP01_01_sc001 | asset1 | asset2 | | SDKTEST_EP01_01_sc002 | asset1 | 必须有命名为镜头(支持别名:shot)的列,所有其他表头不为空的列将视为资产。 镜头的值为 shot.entity 字段 资产的值为 asset.entity 字段 """ ) client = cgtwq.DesktopClient() client.connect() plugin_data = client.plugin.data() # type: ignore db = cgtwq.Database(cast.text(plugin_data.database)) # type: ignore filename = filetools.ask_filename() if not filename: return workbook = openpyxl.load_workbook(filename) for i in xlsxtools.iter_workbook_as_dict(workbook, HEADER_ALIAS): shot = i.get("shot") if shot: _link( db, cast.text(shot), tuple( cast.text(v) for k, v in six.iteritems(i) if (k and v and k != "shot") ), ) else: LOGGER.warning("忽略不支持的数据: %s", cast.binary(i).decode("unicode-escape"))
def _cast_strings(v): # type: (Sequence[Text]) -> Sequence[Text] if isinstance(v, six.binary_type): return (cast.text(v), ) if isinstance(v, six.text_type): return (v, ) return v
def latest() -> Text: # Use `requests` if we have more http related feature resp = cast.instance( urllib.request.urlopen(_VERSION_URL), http.client.HTTPResponse, ) return cast.text(resp.read())
def _upload_image_v5_2(filename, folder, token): # type: (Text, Text, Text) -> ImageInfo """Upload image to server. Args: filename (str): Filename. folder (str): Server upload folder, usually same with project name. token (str): Server session token. Returns: ImageInfo: Uploaded image information. """ filename = cast.text(filename) basename = os.path.basename(filename) data = post( "web_upload_file", { "folder": folder, "type": "project", "method": "convert_image", "filename": basename, }, token=token, files={ "file": (basename, open(filename, "rb"), mimetypes.guess_type(basename)[0]) }, ) assert isinstance(data, dict), type(data) return ImageInfo(data["max"], data["min"], filename)
def get_online_account_id(token=None): # type: (Text) -> Text token = token or cast.text(core.CONFIG["DEFAULT_TOKEN"]) resp = server.call("c_token", "get_all_online_account_id_with_type", token=token) return resp
def _get_status_v5_2(token): # type: (Text) -> Tuple[StatusInfo, ...] token = token or cast.text(CONFIG["DEFAULT_TOKEN"]) resp = http.call("c_status", "get_all", token=token, field_array=StatusInfo._fields) return tuple(StatusInfo(*i) for i in resp)
def get_account_id(token=None): # type: (Text) -> Text """Get account id from token. Args: token (str): Server token Returns: str: Account id. """ token = token or cast.text(core.CONFIG["DEFAULT_TOKEN"]) return server.call("c_token", "get_account_id", token=token)
def get_status(token=None): # type: (Optional[Text]) -> Tuple[StatusInfo, ...] """Get all status on the server. Args: token (str): User token. Returns: tuple[StatusInfo]: Status data. """ token = token or cast.text(CONFIG["DEFAULT_TOKEN"]) if compat.api_level() == compat.API_LEVEL_5_2: return _get_status_v5_2(token) return _get_status_v6_1(token)
def get_software_types(token=None): # type: (Text) -> List[Text] """Get all software types on the server. Args: token ([type], optional): Defaults to None. [description] Returns: list[str] """ token = token or cast.text(CONFIG["DEFAULT_TOKEN"]) resp = http.call("c_status", "get_software_type", token=token) return resp
def _get_shot(path, version_pattern=r"(.+)v(\d+)"): # type: (Text, Text) -> Text """The related shot for this footage. >>> _get_shot('sc_001_v20.nk') u'sc_001' >>> _get_shot('hello world') u'hello world' >>> _get_shot('sc_001_v-1.nk') u'sc_001_v-1' >>> _get_shot('sc001V1.jpg') u'sc001' >>> _get_shot('sc001V1_no_bg.jpg') u'sc001' >>> _get_shot('suv2005_v2_m.jpg') u'suv2005' """ p = PurePath(cast.text(path)) match = re.match(version_pattern, cast.text(p.name), flags=re.I) if not match: return cast.text(p.stem) shot = match.group(1) return shot.strip("_")
def _parse_by_indent(lines, indent=" "): key = "" # type: str values = [] for line in lines: line = cast.text(line) if line.startswith(indent) or line == indent.rstrip(" "): values.append(line[len(indent):]) else: if key: yield (key, values) key = "" values = [] key = line if values: yield (key, values)
def set_fields(self, token=None, **data): # type: (Text, *Any) -> None r"""Set field data for the plug-in. Args: token (str, optional): Defaults to None. User token. \*\*data: Field name as key, Value as value. """ token = token or cast.text(core.CONFIG["DEFAULT_TOKEN"]) self.call("set_one_with_id", token=token, id=self.uuid, field_data_array=data) self.last_fetch_time = None
def login(account, password): # type: (Text, Text) -> AccountInfo """Login on server. Args: account (str): Account name. password (str): Password. Raises: ValueError: When login fail. Returns: AccountInfo: Account information. """ try: resp = server.call( "c_token", "login", account=account, password=password, token="", client_type="py", ) except ValueError as ex: try: raise { "token::login, get account data error": AuthenticateError, "token::login, 密码错误,请检查": AuthenticateError, "token::login, 账号或密码错误, 请检查": AuthenticateError, }[cast.text(ex.args[0])] except (KeyError, IndexError): pass raise ex assert isinstance(resp, dict), type(resp) # Correct server-side typo. resp["password_complexity"] = ( # spell-checker: disable resp.pop("password_comlexity", None) # spell-checker: enable ) _ = [resp.setdefault(i, None) for i in AccountInfo._fields] return AccountInfo(**resp)
def fetch(self, token=None): # type: (Text) -> None """Fetch plugin data from server.""" if self.last_fetch_time and ( self.last_fetch_time - time.time() > core.CONFIG["MIN_FETCH_INTERVAL"]): # type: ignore return token = token or cast.text(core.CONFIG["DEFAULT_TOKEN"]) resp = server.call( "c_plugin", "get_one_with_id", token=token, id=self.uuid, field_array=PluginInfo.fields, ) self._cached_info = PluginInfo(*resp) self.last_fetch_time = time.time()
def _raise_error(result): # type: (Any) -> None if not isinstance(result, dict): return code, type_, data = ( result.get("code"), result.get("type"), result.get("data", result), ) if code is None: return elif code == "1": return elif (code, type_, data) == ("2", "msg", "please login!!!"): raise exceptions.LoginError msg = cast.text(data) if six.PY2: msg = cast.binary(msg) raise ValueError(msg)
def _upload_image_v6_1(filename, folder, token): # type: (Text, Text, Text) -> ImageInfo """Upload image to server. Args: filename (str): Filename. folder (str): Server upload folder, usually same with project name. token (str): Server session token. Returns: ImageInfo: Uploaded image information. """ filename = cast.text(filename) basename = os.path.basename(filename) mtime = os.path.getmtime(filename) data = post( "web_upload_file", { "method": "attachment_upload", "is_web": "Y", "db": folder, "format": "image", "filename": basename, "attachment_argv": { "type": "main", "filename": filename, "modify_time": datetime.datetime.fromtimestamp( mtime, ChinaTimezone() ).strftime("%Y-%m-%d %H:%M:%S"), }, }, token=token, files={ "file": (basename, open(filename, "rb"), mimetypes.guess_type(basename)[0]) }, ) assert isinstance(data, dict), type(data) return ImageInfo(data["max"], data["min"], filename, data["att_id"])
def _upload_image(image, folder, token): # type: (Any, Text, Text) -> ImageInfo if isinstance(image, ImageInfo): return image if isinstance(image, dict): if image.get("max") and image.get("min"): return ImageInfo( max=image["max"], min=image["min"], path=image.get("path"), attachment_id=image.get("att_id"), ) if image.get("path"): image = image["path"] else: raise TypeError( "ImageInfo takes dictionary that has key (max, min, path?).", image.keys(), ) if not isinstance(image, (six.text_type, str)): raise TypeError("Not support such data type.", type(image)) return upload_image(cast.text(image), folder, token)
def filter(cls, filters=None, token=None): # type: (Union[cgtwq.Filter, cgtwq.FilterList], Text) -> List[PluginMeta] """Filter plugins from server. Args: filters (Filter or FilterList, optional): Defaults to None. Plugin filter token (str, optional): Defaults to None. User token. Returns: list[Plugin]: Matched plug-ins. """ filters = filters or Field("#id").has("%") token = token or cast.text(core.CONFIG["DEFAULT_TOKEN"]) filters = FilterList(filters) resp = server.call( "c_plugin", "get_with_filter", token=token, field_array=PluginInfo.fields, filter_array=filters, ) return [cls.from_info(PluginInfo(*i)) for i in resp]
def test_gbk(): assert cast.text(b'\xb2\xe2\xca\xd4', "gbk") == "测试"
def __new__(cls, name, bases, dict_): # type: (Text, Tuple[Any, ...], Any) -> type dict_[_BYTES_KEY] = lambda self: (cast.text( __bytes__) + _as_suffix(self.args)).encode("utf-8") dict_[_STR_KEY] = lambda self: __str__ + _as_suffix(self.args) return type.__new__(cls, name, bases, dict_)
def token(self): # type: () -> Text """User token.""" return self._token or cast.text(core.CONFIG["DEFAULT_TOKEN"])
def _column_by_alias(header, header_alias): # type: (Any, Dict[Text, Tuple[Text, ...]]) -> Text for k, v in six.iteritems(header_alias): if header in v: return k return cast.text(header)
def _handle_windows_line_ending(lines): for i in lines: i = cast.text(i) yield i.strip("\r\n")
def typing_from_help(text): return "\n".join(iterate_typing_from_help(cast.text(text).splitlines()))
def test_binary(): assert cast.text(b"value") == "value"
def test_text(): assert cast.text("测试") == "测试"
def test_utf8(): assert cast.text(b'\xe6\xb5\x8b\xe8\xaf\x95') == "测试"
def test_dict(): assert cast.text(dict(a=1)) == "{'a': 1}"
def test_int(): assert cast.text(1) == "1"