def cmd_pull(argv, path_to_tx): """Pull files from remote server to local repository""" parser = pull_parser() options = parser.parse_args(argv) if options.fetchall and options.languages: parser.error("You can't user a language filter along with the " "-a|--all option") languages = parse_csv_option(options.languages) resources = parse_csv_option(options.resources) pseudo = options.pseudo # Should we download as xliff? xliff = options.xliff parallel = options.parallel skip = options.skip_errors minimum_perc = options.minimum_perc or None _go_to_dir(path_to_tx) # instantiate the project.Project prj = project.Project(path_to_tx) branch = get_branch_from_options(options, prj.root) prj.pull( languages=languages, resources=resources, overwrite=options.overwrite, fetchall=options.fetchall, fetchsource=options.fetchsource, force=options.force, skip=skip, minimum_perc=minimum_perc, mode=options.mode, pseudo=pseudo, xliff=xliff, branch=branch, parallel=parallel, no_interactive=options.no_interactive, ) logger.info("Done.")
def cmd_pull(argv, path_to_tx): "Pull files from remote server to local repository" parser = pull_parser() (options, args) = parser.parse_args(argv) if options.fetchall and options.languages: parser.error("You can't user a language filter along with the"\ " -a|--all option") languages = parse_csv_option(options.languages) resources = parse_csv_option(options.resources) pseudo = options.pseudo skip = options.skip_errors minimum_perc = options.minimum_perc or None try: _go_to_dir(path_to_tx) except UnInitializedError as e: utils.logger.error(e) return # instantiate the project.Project prj = project.Project(path_to_tx) prj.pull( languages=languages, resources=resources, overwrite=options.overwrite, fetchall=options.fetchall, fetchsource=options.fetchsource, force=options.force, skip=skip, minimum_perc=minimum_perc, mode=options.mode, pseudo=pseudo ) logger.info("Done.")
def cmd_status(argv, path_to_tx): "Print status of current project" parser = status_parser() (options, args) = parser.parse_args(argv) resources = parse_csv_option(options.resources) prj = project.Project(path_to_tx) resources = prj.get_chosen_resources(resources) resources_num = len(resources) for idx, res in enumerate(resources): p, r = res.split('.') logger.info("%s -> %s (%s of %s)" % (p, r, idx + 1, resources_num)) logger.info("Translation Files:") slang = prj.get_resource_option(res, 'source_lang') sfile = prj.get_resource_option(res, 'source_file') or "N/A" lang_map = prj.get_resource_lang_mapping(res) logger.info(" - %s: %s (%s)" % (utils.color_text(slang, "RED"), sfile, utils.color_text("source", "YELLOW"))) files = prj.get_resource_files(res) fkeys = files.keys() fkeys.sort() for lang in fkeys: local_lang = lang if lang in lang_map.values(): local_lang = lang_map.flip[lang] logger.info(" - %s: %s" % (utils.color_text(local_lang, "RED"), files[lang])) logger.info("")
def getset_host_credentials(self, host, user=None, password=None): """ Read .transifexrc and report user,pass for a specific host else ask the user for input. """ try: username = self.txrc.get(host, 'username') passwd = self.txrc.get(host, 'password') except (configparser.NoOptionError, configparser.NoSectionError): logger.info("No entry found for host %s. Creating..." % host) username = user or input("Please enter your transifex username: "******"Please enter your transifex username: "******"Updating %s file..." % self.txrc_file) self.txrc.add_section(host) self.txrc.set(host, 'username', username) self.txrc.set(host, 'password', passwd) self.txrc.set(host, 'token', '') self.txrc.set(host, 'hostname', host) return username, passwd
def _delete_translations(self, project_details, resource, stats, languages): """Delete the specified translations for the specified resource.""" logger.info("Deleting translations from resource %s:" % resource) for language in languages: self._delete_translation( project_details, resource, stats, language )
def cmd_init(argv, path_to_tx): "Initialize a new transifex project." parser = init_parser() (options, args) = parser.parse_args(argv) if len(args) > 1: parser.error("Too many arguments were provided. Aborting...") if args: path_to_tx = args[0] else: path_to_tx = os.getcwd() if os.path.isdir(os.path.join(path_to_tx,".tx")): logger.info("tx: There is already a tx folder!") reinit = raw_input("Do you want to delete it and reinit the project? [y/N]: ") while (reinit != 'y' and reinit != 'Y' and reinit != 'N' and reinit != 'n' and reinit != ''): reinit = raw_input("Do you want to delete it and reinit the project? [y/N]: ") if not reinit or reinit in ['N', 'n', 'NO', 'no', 'No']: return # Clean the old settings # FIXME: take a backup else: rm_dir = os.path.join(path_to_tx, ".tx") shutil.rmtree(rm_dir) logger.info("Creating .tx folder...") os.mkdir(os.path.join(path_to_tx,".tx")) # Handle the credentials through transifexrc home = os.path.expanduser("~") txrc = os.path.join(home, ".transifexrc") config = OrderedRawConfigParser() default_transifex = "https://www.transifex.net" transifex_host = options.host or raw_input("Transifex instance [%s]: " % default_transifex) if not transifex_host: transifex_host = default_transifex if not transifex_host.startswith(('http://', 'https://')): transifex_host = 'https://' + transifex_host config_file = os.path.join(path_to_tx, ".tx", "config") if not os.path.exists(config_file): # The path to the config file (.tx/config) logger.info("Creating skeleton...") config = OrderedRawConfigParser() config.add_section('main') config.set('main', 'host', transifex_host) # Touch the file if it doesn't exist logger.info("Creating config file...") fh = open(config_file, 'w') config.write(fh) fh.close() prj = project.Project(path_to_tx) prj.getset_host_credentials(transifex_host, user=options.user, password=options.password) prj.save() logger.info("Done.")
def pull(): resourceLink = request.form["resources"] resources = resourceLink.split("*//") prj = project.Project(path_to_tx) #resource = request.args.get('resource') logger.info(resources[-1]) #prj.pull(resources=[resource], fetchall=True, skip=True) return jsonify(result="OK")
def modify_expr(): """Modify the file filter of a resource.""" prj = project.Project(path_to_tx) res_id = request.form['res_id'] expr = request.form['value'] logger.info("Changing expression of %s to %s" % (res_id, expr)) prj.config.set("%s" % res_id, "file_filter", expr) prj.save() return expr
def cmd_delete(argv, path_to_tx): "Delete an accessible resource or translation in a remote server." parser = delete_parser() (options, args) = parser.parse_args(argv) languages = parse_csv_option(options.languages) resources = parse_csv_option(options.resources) skip = options.skip_errors force = options.force_delete prj = project.Project(path_to_tx) prj.delete(resources, languages, skip, force) logger.info("Done.")
def push(): resourceLink = request.form["resources"] resources = resourceLink.split("*//") logger.info("zaab") prj = project.Project(path_to_tx) try: prj.push(resources=resources, source=True) prj.push(resources=resources, skip=True, translations=True, source=False) return "success" except: return "failed"
def cmd_init(argv, path_to_tx): """Initialize a new Transifex project.""" parser = init_parser() options = parser.parse_args(argv) path_to_tx = options.path_to_tx or os.getcwd() print(messages.init_intro) save = options.save # if we already have a config file and we are not told to override it # in the args we have to ask config_file = os.path.join(path_to_tx, ".tx", "config") if os.path.isfile(config_file): if not save: if options.no_interactive: parser.error("Project already initialized.") logger.info(messages.init_initialized) if not utils.confirm(messages.init_reinit): return os.remove(config_file) if not os.path.isdir(os.path.join(path_to_tx, ".tx")): logger.info("Creating .tx folder...") os.mkdir(os.path.join(path_to_tx, ".tx")) default_transifex = "https://www.transifex.com" transifex_host = options.host or default_transifex if not transifex_host.startswith(('http://', 'https://')): transifex_host = 'https://' + transifex_host if not os.path.exists(config_file): # Handle the credentials through transifexrc config = OrderedRawConfigParser() config.add_section('main') config.set('main', 'host', transifex_host) # Touch the file if it doesn't exist logger.info("Creating config file...") fh = open(config_file, 'w') config.write(fh) fh.close() if not options.skipsetup and not options.no_interactive: logger.info(messages.running_tx_set) cmd_config([], path_to_tx) else: prj = project.Project(path_to_tx) prj.getset_host_credentials(transifex_host, username=options.user, password=options.password, token=options.token, force=options.save, no_interactive=options.no_interactive) prj.save() logger.info("Done.")
def cmd_help(argv, path_to_tx): """List all available commands""" parser = help_parser() (options, args) = parser.parse_args(argv) if len(args) > 1: parser.error("Multiple arguments received. Exiting...") # Get all commands fns = utils.discover_commands() # Print help for specific command if len(args) == 1: try: fns[argv[0]](['--help'], path_to_tx) except KeyError: utils.logger.error("Command %s not found" % argv[0]) # or print summary of all commands # the code below will only be executed if the KeyError exception is thrown # becuase in all other cases the function called with --help will exit # instead of return here keys = fns.keys() keys.sort() logger.info("Transifex command line client.\n") logger.info("Available commands are:") for key in keys: logger.info(" %-15s\t%s" % (key, fns[key].func_doc)) logger.info("\nFor more information run %s command --help" % sys.argv[0])
def _set_source_file(path_to_tx, resource, lang, path_to_file): """Reusable method to set source file.""" proj, res = resource.split('.') if not proj or not res: raise Exception("\"%s.%s\" is not a valid resource identifier. " "It should be in the following format " "project_slug.resource_slug." % (proj, res)) if not lang: raise Exception("You haven't specified a source language.") try: _go_to_dir(path_to_tx) except UnInitializedError as e: utils.logger.error(e) return if not os.path.exists(path_to_file): raise Exception("tx: File ( %s ) does not exist." % os.path.join(path_to_tx, path_to_file)) # instantiate the project.Project prj = project.Project(path_to_tx) root_dir = os.path.abspath(path_to_tx) if root_dir not in os.path.normpath(os.path.abspath(path_to_file)): raise Exception("File must be under the project root directory.") logger.info("Setting source file for resource %s.%s ( %s -> %s )." % ( proj, res, lang, path_to_file)) path_to_file = os.path.relpath(path_to_file, root_dir) prj = project.Project(path_to_tx) # FIXME: Check also if the path to source file already exists. try: try: prj.config.get("%s.%s" % (proj, res), "source_file") except configparser.NoSectionError: prj.config.add_section("%s.%s" % (proj, res)) except configparser.NoOptionError: pass finally: prj.config.set( "%s.%s" % (proj, res), "source_file", posix_path(path_to_file) ) prj.config.set("%s.%s" % (proj, res), "source_lang", lang) prj.save()
def pullResource(): #pull for particular resource and particular languages of that resource #languages are in one string and we split them by *// also at the end of that string we have the resource name resourceLanguages = request.form["resourceLanguages"] languages = resourceLanguages.split("*//") resource = languages[-1] logger.info("shit") languages.pop() prj = project.Project(path_to_tx) try: prj.pull(resources=[resource], fetchall=True, skip=True, languages=languages) return "success" except: return "failed"
def _get_transifex_file(self, directory=None): """Fetch the path of the .transifexrc file. It is in the home directory ofthe user by default. """ if directory is None: directory = os.path.expanduser('~') txrc_file = os.path.join(directory, ".transifexrc") logger.debug(".transifexrc file is at %s" % directory) if not os.path.exists(txrc_file): msg = "No authentication data found." logger.info(msg) mask = os.umask(077) open(txrc_file, 'w').close() os.umask(mask) return txrc_file
def _migrate_txrc_file(self, txrc): """Migrate the txrc file, if needed.""" for section in txrc.sections(): orig_hostname = txrc.get(section, 'hostname') hostname = visit_hostname(orig_hostname) if hostname != orig_hostname: msg = "Hostname %s should be changed to %s." logger.info(msg % (orig_hostname, hostname)) if (sys.stdin.isatty() and sys.stdout.isatty() and confirm('Change it now? ', default=True)): txrc.set(section, 'hostname', hostname) msg = 'Hostname changed' logger.info(msg) else: hostname = orig_hostname self._save_txrc_file(txrc) return txrc
def _auto_remote(path_to_tx, url): """ Initialize a remote project/resource to the current directory. """ logger.info("Auto configuring local project from remote URL...") type, vars = utils.parse_tx_url(url) prj = project.Project(path_to_tx) username, password = prj.getset_host_credentials(vars['hostname']) if type == 'project': logger.info("Getting details for project %s" % vars['project']) proj_info = utils.get_details('project_details', username, password, hostname = vars['hostname'], project = vars['project']) resources = [ '.'.join([vars['project'], r['slug']]) for r in proj_info['resources'] ] logger.info("%s resources found. Configuring..." % len(resources)) elif type == 'resource': logger.info("Getting details for resource %s" % vars['resource']) resources = [ '.'.join([vars['project'], vars['resource']]) ] else: raise("Url '%s' is not recognized." % url) for resource in resources: logger.info("Configuring resource %s." % resource) proj, res = resource.split('.') res_info = utils.get_details('resource_details', username, password, hostname = vars['hostname'], project = proj, resource=res) try: source_lang = res_info['source_language_code'] i18n_type = res_info['i18n_type'] except KeyError: raise Exception("Remote server seems to be running an unsupported version" " of Transifex. Either update your server software of fallback" " to a previous version of transifex-client.") prj.set_remote_resource( resource=resource, host = vars['hostname'], source_lang = source_lang, i18n_type = i18n_type) prj.save()
def cmd_pull(argv, path_to_tx): "Pull files from remote server to local repository" parser = pull_parser() (options, args) = parser.parse_args(argv) logger.info(argv); if options.fetchall and options.languages: parser.error("You can't user a language filter along with the"\ " -a|--all option") languages = parse_csv_option(options.languages) logger.info(languages); resources = parse_csv_option(options.resources) skip = options.skip_errors minimum_perc = options.minimum_perc or None try: _go_to_dir(path_to_tx) except UnInitializedError, e: utils.logger.error(e) return
def cmd_push(argv, path_to_tx): "Push local files to remote server" parser = push_parser() (options, args) = parser.parse_args(argv) force_creation = options.force_creation languages = parse_csv_option(options.languages) resources = parse_csv_option(options.resources) skip = options.skip_errors prj = project.Project(path_to_tx) if not (options.push_source or options.push_translations): parser.error("You need to specify at least one of the -s|--source," " -t|--translations flags with the push command.") prj.push( force=force_creation, resources=resources, languages=languages, skip=skip, source=options.push_source, translations=options.push_translations, no_interactive=options.no_interactive ) logger.info("Done.")
def bare_set(path_to_tx, options): filename = options.filename # Calculate relative path path_to_file = os.path.relpath(filename, path_to_tx) if options.is_source: resource = options.resource _set_source_file(path_to_tx, resource, options.language, path_to_file) elif options.resource or options.language: resource = options.resource lang = options.language _go_to_dir(path_to_tx) _set_translation(path_to_tx, resource, lang, path_to_file) _set_mode(options.resource, options.mode, path_to_tx) _set_type(options.resource, options.i18n_type, path_to_tx) _set_minimum_perc(options.resource, options.minimum_perc, path_to_tx) logger.info("Done.")
def _set_translation(path_to_tx, resource, lang, path_to_file): """Reusable method to set translation file.""" proj, res = resource.split('.') if not project or not resource: raise Exception("\"%s\" is not a valid resource identifier. " "It should be in the following format " "project_slug.resource_slug." % resource) try: _go_to_dir(path_to_tx) except UnInitializedError as e: utils.logger.error(e) return # Warn the user if the file doesn't exist if not os.path.exists(path_to_file): logger.info("Warning: File '%s' doesn't exist." % path_to_file) # instantiate the project.Project prj = project.Project(path_to_tx) root_dir = os.path.abspath(path_to_tx) if root_dir not in os.path.normpath(os.path.abspath(path_to_file)): raise Exception("File must be under the project root directory.") if lang == prj.config.get("%s.%s" % (proj, res), "source_lang"): raise Exception("tx: You cannot set translation file for " "the source language. Source languages contain " "the strings which will be translated!") logger.info("Updating translations for resource %s ( %s -> %s )." % ( resource, lang, path_to_file)) path_to_file = os.path.relpath(path_to_file, root_dir) prj.config.set( "%s.%s" % (proj, res), "trans.%s" % lang, posix_path(path_to_file) ) prj.save()
def rename(): """Rename a resource.""" prj = project.Project(path_to_tx) res_old = request.form['elementid'] res_new = request.form['value'] # Raise error with invalid resource ID if res_new.count('.') != 1: error_msg = u"New resource ID should have one dot in it." flash(error_msg, 'error') logger.warning(error_msg) return(res_old) # Avoid deleting the same resource if unchanged: if res_new == res_old: return(res_old) logger.info("Renaming %s to %s" % (res_old, res_new)) prj.config._sections[res_new] = prj.config._sections[res_old] prj.config._sections.pop(res_old) prj.save() return res_new
def _delete_translation(self, project_details, resource, stats, language): """Delete a specific translation from the specified resource.""" project_slug, resource_slug = resource.split('.', 1) if language not in stats: if not self.skip: msg = "Skipping %s: Translation does not exist." logger.warning(msg % (language)) return if not self.force: teams = project_details['teams'] if language in teams: msg = ( "Skipping %s: Unable to delete translation because it is " "associated with a team.\nPlease use -f or --force option " "to delete this translation." ) logger.warning(msg % language) return if int(stats[language]['translated_entities']) > 0: msg = ( "Skipping %s: Unable to delete translation because it " "is not empty.\nPlease use -f or --force option to delete " "this translation." ) logger.warning(msg % language) return try: self.do_url_request( 'delete_translation', language=language, method="DELETE" ) msg = "Deleted language %s from resource %s of project %s." logger.info(msg % (language, resource_slug, project_slug)) except Exception as e: msg = "Unable to delete translation %s" logger.error(msg % language) if isinstance(e, SSLError) or not self.skip: raise
def cmd_push(argv, path_to_tx): """Push local files to remote server""" parser = push_parser() options = parser.parse_args(argv) force_creation = options.force_creation languages = parse_csv_option(options.languages) resources = parse_csv_option(options.resources) skip = options.skip_errors xliff = options.xliff parallel = options.parallel prj = project.Project(path_to_tx) if not (options.push_source or options.push_translations): parser.error("You need to specify at least one of the -s|--source, " "-t|--translations flags with the push command.") branch = get_branch_from_options(options, prj.root) prj.push( force=force_creation, resources=resources, languages=languages, skip=skip, source=options.push_source, translations=options.push_translations, no_interactive=options.no_interactive, xliff=xliff, branch=branch, parallel=parallel ) logger.info("Done.")
def _delete_resource(self, project_details, resource, stats, *args): """Delete a resource from Transifex.""" project_slug, resource_slug = resource.split('.', 1) project_resource_slugs = [ r['slug'] for r in project_details['resources'] ] logger.info("Deleting resource %s:" % resource) if resource_slug not in project_resource_slugs: if not self.skip: msg = "Skipping: %s : Resource does not exist." logger.info(msg % resource) return if not self.force: slang = self.get_resource_option(resource, 'source_lang') for language in stats: if language == slang: continue if int(stats[language]['translated_entities']) > 0: msg = ( "Skipping: %s : Unable to delete resource because it " "has a not empty %s translation.\nPlease use -f or " "--force option to delete this resource." ) logger.info(msg % (resource, language)) return try: self.do_url_request('delete_resource', method="DELETE") self.config.remove_section(resource) self.save() msg = "Deleted resource %s of project %s." logger.info(msg % (resource_slug, project_slug)) except Exception as e: msg = "Unable to delete resource %s of project %s." logger.error(msg % (resource_slug, project_slug)) if isinstance(e, SSLError) or not self.skip: raise
def _auto_remote(path_to_tx, url): """Initialize a remote project/resource to the current directory.""" logger.info("Auto configuring local project from remote URL...") type, vars = utils.parse_tx_url(url) prj = project.Project(path_to_tx) username, password = prj.getset_host_credentials(vars['hostname']) if type.startswith('project'): logger.info("Getting details for project %s" % vars['project']) proj_info = utils.get_details( 'project_details', username, password, hostname=vars['hostname'], project=vars['project']) resources = ['.'.join([vars['project'], r['slug']]) for r in proj_info['resources']] logger.info("%s resources found. Configuring..." % len(resources)) elif type == 'release': logger.info("Getting details for release %s" % vars['release']) rel_info = utils.get_details( 'release_details', username, password, hostname=vars['hostname'], project=vars['project'], release=vars['release']) resources = [] for r in rel_info['resources']: if 'project' in r: resources.append('.'.join([r['project']['slug'], r['slug']])) else: resources.append('.'.join([vars['project'], r['slug']])) logger.info("%s resources found. Configuring..." % len(resources)) elif type.startswith('resource'): logger.info("Getting details for resource %s" % vars['resource']) resources = ['.'.join([vars['project'], vars['resource']])] else: raise Exception("Url '%s' is not recognized." % url) for resource in resources: logger.info("Configuring resource %s." % resource) proj, res = resource.split('.') res_info = utils.get_details( 'resource_details', username, password, hostname=vars['hostname'], project=proj, resource=res) try: source_lang = res_info['source_language_code'] i18n_type = res_info['i18n_type'] except KeyError: raise Exception("Remote server seems to be running an unsupported " "version of Transifex. Either update your server " "software of fallback to a previous version " "of transifex-client.") prj.set_remote_resource( resource=resource, host=vars['hostname'], source_lang=source_lang, i18n_type=i18n_type) prj.save()
def cmd_wui(argv, path_to_tx): """Run a web ui.""" from flask import Flask, flash, jsonify, render_template, request import webbrowser app = Flask(__name__) app.secret_key = 'blahdiblah' @app.route('/tx/home/') def home(name=None): "Print status of current project" from txclib import get_version txc_version = get_version() prj = project.Project(path_to_tx) # Let's create a resource list from our config file res_list = [] prev_proj = '' for idx, res in enumerate(prj.get_resource_list()): hostname = prj.get_resource_host(res) p, r = res.split('.') p_url = '%s/projects/p/%s' % (hostname, p) r_url = '%s/resource/%s' % (p_url, r) sfile = prj.get_resource_option(res, 'source_file') or "N/A" expr = prj.config.get(res, "file_filter") expr_highlight = expr.replace('<lang>', '<span class="lang"><lang></span>') res_list.append({'id': res, 'p_name': p, 'p_url': p_url, 'r_name': r, 'r_url': r_url, 'source_file': sfile, 'expr': expr, 'expr_highlight': expr_highlight}) res_list = sorted(res_list, key=lambda k: k['id']) username, password = prj.getset_host_credentials(hostname) return render_template('index.html', res_list=res_list, txc_version=txc_version, username=username, password=password) @app.route('/tx/_pull', methods=['GET', 'POST']) def pull(): #pull all selected resources #resources are all in one string and we split them by *// resourceLink = request.form["resources"] resources = resourceLink.split("*//") prj = project.Project(path_to_tx) prj.pull(resources=resources, fetchall=True, skip=True ) return jsonify(result="OK") @app.route('/tx/_pullResource', methods=['GET', 'POST']) def pullResource(): #pull for particular resource and particular languages of that resource #languages are in one string and we split them by *// also at the end of that string we have the resource name resourceLanguages = request.form["resourceLanguages"] languages = resourceLanguages.split("*//") resource = languages[-1] logger.info("shit") languages.pop() prj = project.Project(path_to_tx) try: prj.pull(resources=[resource], fetchall=True, skip=True, languages=languages) return "success" except: return "failed" @app.route('/tx/_pushResource', methods=['GET', 'POST']) def pushResource(): #push for particular reource and particular languages of that resource #languges are in one string and we split them by *// also at the end of tha string we have the resource name locale = request.form["locales"] prj = project.Project(path_to_tx) locales = locale.split("*//") resource = locales[-1] locales.pop() try: prj.push(resources=[resource], source=True) prj.push(resources=[resource], skip=True, translations=True, source=False, languages=locales) return "success" except: return "failed" @app.route('/tx/resource/<resouce_data>') def resource(resouce_data): from txclib import get_version txc_version = get_version() data = resouce_data.split("***") resource_name = data[0] username = data[1] password = data[2] return render_template('resource.html', txc_version=txc_version, username=username, password=password, resource_name=resource_name) @app.route('/tx/_push', methods=['GET', 'POST']) def push(): #push all selected resources #resource names are in one string and split them with *// resourceLink = request.form["resources"] resources = resourceLink.split("*//") prj = project.Project(path_to_tx) try: prj.push(resources=resources, source=True) prj.push(resources=resources, skip=True, translations=True, source=False) return "success" except: return "failed" @app.route('/_rename', methods=['POST']) def rename(): """Rename a resource.""" prj = project.Project(path_to_tx) res_old = request.form['elementid'] res_new = request.form['value'] # Raise error with invalid resource ID if res_new.count('.') != 1: error_msg = u"New resource ID should have one dot in it." flash(error_msg, 'error') logger.warning(error_msg) return(res_old) # Avoid deleting the same resource if unchanged: if res_new == res_old: return(res_old) logger.info("Renaming %s to %s" % (res_old, res_new)) prj.config._sections[res_new] = prj.config._sections[res_old] prj.config._sections.pop(res_old) prj.save() return res_new @app.route('/_modify_expr', methods=['POST']) def modify_expr(): """Modify the file filter of a resource.""" prj = project.Project(path_to_tx) res_id = request.form['res_id'] expr = request.form['value'] logger.info("Changing expression of %s to %s" % (res_id, expr)) prj.config.set("%s" % res_id, "file_filter", expr) prj.save() return expr logger.info("Running web UI. Please navigate to http://localhost:5000/") webbrowser.open("http://localhost:5000/tx/home", new=0) app.run(debug=True)
if lang in lang_map.values(): remote_lang = lang_map.flip[lang] else: remote_lang = lang local_file = files[local_lang] kwargs = { 'lang': remote_lang, 'stats': stats, 'local_file': local_file, 'force': force, } if not self._should_push_translation(**kwargs): msg = "Skipping '%s' translation (file: %s)." logger.info(msg % (color_text(lang, "RED"), local_file)) continue msg = "Pushing '%s' translations (file: %s)" logger.warning( msg % (color_text(remote_lang, "RED"), local_file)) try: self.do_url_request( 'push_translation', multipart=True, method='PUT', files=[("%s;%s" % (resource_slug, remote_lang), self.get_full_path(local_file))], language=remote_lang) logger.debug("Translation %s pushed." % remote_lang) except Exception, e:
def _auto_local(path_to_tx, resource, source_language, expression, execute=False, source_file=None, regex=False): """Auto configure local project.""" # The path everything will be relative to curpath = os.path.abspath(os.curdir) # Force expr to be a valid regex expr (escaped) but keep <lang> intact expr_re = utils.regex_from_filefilter(expression, curpath) expr_rec = re.compile(expr_re) if not execute: logger.info("Only printing the commands which will be run if the " "--execute switch is specified.") # First, let's construct a dictionary of all matching files. # Note: Only the last matching file of a language will be stored. translation_files = {} for f_path in files_in_project(curpath): match = expr_rec.match(posix_path(f_path)) if match: lang = match.group(1) if lang == source_language and not source_file: source_file = f_path else: translation_files[lang] = f_path if not source_file: raise Exception("Could not find a source language file. Please run " "set --source manually and then re-run this command " "or provide the source file with the -s flag.") if execute: logger.info("Updating source for resource %s ( %s -> %s )." % ( resource, source_language, os.path.relpath( source_file, path_to_tx) )) _set_source_file(path_to_tx, resource, source_language, os.path.relpath(source_file, path_to_tx)) else: logger.info('\ntx set --source -r %(res)s -l %(lang)s %(file)s\n' % { 'res': resource, 'lang': source_language, 'file': os.path.relpath(source_file, curpath)}) prj = project.Project(path_to_tx) root_dir = os.path.abspath(path_to_tx) if execute: try: prj.config.get("%s" % resource, "source_file") except configparser.NoSectionError: raise Exception("No resource with slug \"%s\" was found.\nRun " "'tx set --auto-local -r %s \"expression\"' to " "do the initial configuration." % resource) # Now let's handle the translation files. if execute: logger.info("Updating file expression for resource %s ( %s )." % ( resource, expression)) # Eval file_filter relative to root dir file_filter = posix_path( os.path.relpath(os.path.join(curpath, expression), path_to_tx) ) prj.config.set("%s" % resource, "file_filter", file_filter) else: for (lang, f_path) in sorted(translation_files.items()): logger.info('tx set -r %(res)s -l %(lang)s %(file)s' % { 'res': resource, 'lang': lang, 'file': os.path.relpath(f_path, curpath)}) if execute: prj.save()
@app.route('/tx/_pull', methods=['GET', 'POST']) def pull(): resourceLink = request.form["resources"] resources = resourceLink.split("*//") prj = project.Project(path_to_tx) #resource = request.args.get('resource') logger.info(resources[-1]) #prj.pull(resources=[resource], fetchall=True, skip=True) return jsonify(result="OK") @app.route('/tx/_push', methods=['GET', 'POST']) def push(): resourceLink = request.form["resources"] resources = resourceLink.split("*//") logger.info("zaab") prj = project.Project(path_to_tx) try: prj.push(resources=resources, source=True) prj.push(resources=resources, skip=True, translations=True, source=False) return "success" except: return "failed" logger.info("Running web UI. Please navigate to http://localhost:5000/") webbrowser.open("http://localhost:5000/tx/home", new=0)
def cmd_set(argv, path_to_tx): """Add local or remote files under transifex""" parser = set_parser() (options, args) = parser.parse_args(argv) # Implement options/args checks # TODO !!!!!!! if options.local: try: expression = args[0] except IndexError: parser.error("Please specify an expression.") if not options.resource: parser.error("Please specify a resource") if not options.source_language: parser.error("Please specify a source language.") if '<lang>' not in expression: parser.error("The expression you have provided is not valid.") if not utils.valid_slug(options.resource): parser.error("Invalid resource slug. The format is <project_slug>" ".<resource_slug> and the valid characters include" " [_-\w].") _auto_local(path_to_tx, options.resource, source_language=options.source_language, expression=expression, source_file=options.source_file, execute=options.execute, regex=False) if options.execute: _set_minimum_perc(options.resource, options.minimum_perc, path_to_tx) _set_mode(options.resource, options.mode, path_to_tx) _set_type(options.resource, options.i18n_type, path_to_tx) return if options.remote: try: url = args[0] except IndexError: parser.error("Please specify an remote url") _auto_remote(path_to_tx, url) _set_minimum_perc(options.resource, options.minimum_perc, path_to_tx) _set_mode(options.resource, options.mode, path_to_tx) return if options.is_source: resource = options.resource if not resource: parser.error("You must specify a resource name with the " "-r|--resource flag.") lang = options.language if not lang: parser.error("Please specify a source language.") if len(args) != 1: parser.error("Please specify a file.") if not utils.valid_slug(resource): parser.error("Invalid resource slug. The format is <project_slug>" ".<resource_slug> and the valid characters include " "[_-\w].") file = args[0] # Calculate relative path path_to_file = os.path.relpath(file, path_to_tx) _set_source_file(path_to_tx, resource, options.language, path_to_file) elif options.resource or options.language: resource = options.resource lang = options.language if len(args) != 1: parser.error("Please specify a file") # Calculate relative path path_to_file = os.path.relpath(args[0], path_to_tx) try: _go_to_dir(path_to_tx) except UnInitializedError as e: utils.logger.error(e) return if not utils.valid_slug(resource): parser.error("Invalid resource slug. The format is <project_slug>" ".<resource_slug> and the valid characters include " "[_-\w].") _set_translation(path_to_tx, resource, lang, path_to_file) _set_mode(options.resource, options.mode, path_to_tx) _set_type(options.resource, options.i18n_type, path_to_tx) _set_minimum_perc(options.resource, options.minimum_perc, path_to_tx) logger.info("Done.") return
def MSG(msg, verbose=1): """ STDOUT logging function """ logger.info('%s' % msg)
def _auto_local(path_to_tx, resource, source_language, expression, execute=False, source_file=None, regex=False): """Auto configure local project.""" # The path everything will be relative to curpath = os.path.abspath(os.curdir) # Force expr to be a valid regex expr (escaped) but keep <lang> intact expr_re = utils.regex_from_filefilter(expression, curpath) expr_rec = re.compile(expr_re) if not execute: logger.info("Only printing the commands which will be run if the " "--execute switch is specified.") # First, let's construct a dictionary of all matching files. # Note: Only the last matching file of a language will be stored. translation_files = {} for root, dirs, files in os.walk(curpath): for f in files: f_path = os.path.abspath(os.path.join(root, f)) match = expr_rec.match(f_path) if match: lang = match.group(1) f_path = os.path.abspath(f_path) if lang == source_language and not source_file: source_file = f_path else: translation_files[lang] = f_path if not source_file: raise Exception("Could not find a source language file. Please run" " set --source manually and then re-run this command or provide" " the source file with the -s flag.") if execute: logger.info("Updating source for resource %s ( %s -> %s )." % (resource, source_language, relpath(source_file, path_to_tx))) _set_source_file(path_to_tx, resource, source_language, relpath(source_file, path_to_tx)) else: logger.info('\ntx set --source -r %(res)s -l %(lang)s %(file)s\n' % { 'res': resource, 'lang': source_language, 'file': relpath(source_file, curpath)}) prj = project.Project(path_to_tx) root_dir = os.path.abspath(path_to_tx) if execute: try: prj.config.get("%s" % resource, "source_file") except ConfigParser.NoSectionError: raise Exception("No resource with slug \"%s\" was found.\nRun 'tx set --auto" "-local -r %s \"expression\"' to do the initial configuration." % resource) # Now let's handle the translation files. if execute: logger.info("Updating file expression for resource %s ( %s )." % (resource, expression)) # Eval file_filter relative to root dir file_filter = relpath(os.path.join(curpath, expression), path_to_tx) prj.config.set("%s" % resource, "file_filter", file_filter) else: for (lang, f_path) in sorted(translation_files.items()): logger.info('tx set -r %(res)s -l %(lang)s %(file)s' % { 'res': resource, 'lang': lang, 'file': relpath(f_path, curpath)}) if execute: prj.save()
try: _go_to_dir(path_to_tx) except UnInitializedError, e: utils.logger.error(e) return if not utils.valid_slug(resource): parser.error("Invalid resource slug. The format is <project_slug>"\ ".<resource_slug> and the valid characters include [_-\w].") _set_translation(path_to_tx, resource, lang, path_to_file) _set_mode(options.resource, options.mode, path_to_tx) _set_type(options.resource, options.i18n_type, path_to_tx) _set_minimum_perc(options.resource, options.minimum_perc, path_to_tx) logger.info("Done.") return def _auto_local(path_to_tx, resource, source_language, expression, execute=False, source_file=None, regex=False): """Auto configure local project.""" # The path everything will be relative to curpath = os.path.abspath(os.curdir) # Force expr to be a valid regex expr (escaped) but keep <lang> intact expr_re = utils.regex_from_filefilter(expression, curpath) expr_rec = re.compile(expr_re) if not execute: logger.info("Only printing the commands which will be run if the "
def push(self, source=False, translations=False, force=False, resources=[], languages=[], skip=False, no_interactive=False): """ Push all the resources """ resource_list = self.get_chosen_resources(resources) self.skip = skip self.force = force for resource in resource_list: push_languages = [] project_slug, resource_slug = resource.split('.', 1) files = self.get_resource_files(resource) slang = self.get_resource_option(resource, 'source_lang') sfile = self.get_source_file(resource) lang_map = self.get_resource_lang_mapping(resource) host = self.get_resource_host(resource) logger.debug("Language mapping is: %s" % lang_map) logger.debug("Using host %s" % host) self.url_info = { 'host': host, 'project': project_slug, 'resource': resource_slug } logger.info("Pushing translations for resource %s:" % resource) stats = self._get_stats_for_resource() if force and not no_interactive: answer = input("Warning: By using --force, the uploaded" " files will overwrite remote translations, even if they" " are newer than your uploaded files.\nAre you sure you" " want to continue? [y/N] ") if not answer in ["", 'Y', 'y', "yes", 'YES']: return if source: if sfile is None: logger.error("You don't seem to have a proper source file" " mapping for resource %s. Try without the --source" " option or set a source file first and then try again." % resource) continue # Push source file try: logger.warning("Pushing source file (%s)" % sfile) if not self._resource_exists(stats): logger.info("Resource does not exist. Creating...") fileinfo = "%s;%s" % (resource_slug, slang) filename = self.get_full_path(sfile) self._create_resource(resource, project_slug, fileinfo, filename) self.do_url_request( 'push_source', multipart=True, method="PUT", files=[( "%s;%s" % (resource_slug, slang) , self.get_full_path(sfile) )], ) except Exception as e: if isinstance(e, SSLError) or not skip: raise else: logger.error(e) else: try: self.do_url_request('resource_details') except Exception as e: if isinstance(e, SSLError): raise code = getattr(e, 'code', None) if code == 404: msg = "Resource %s doesn't exist on the server." logger.error(msg % resource) continue if translations: # Check if given language codes exist if not languages: push_languages = list(files.keys()) else: push_languages = [] f_langs = list(files.keys()) for l in languages: if l in list(lang_map.keys()): l = lang_map[l] push_languages.append(l) if l not in f_langs: msg = "Warning: No mapping found for language code '%s'." logger.error(msg % color_text(l,"RED")) logger.debug("Languages to push are %s" % push_languages) # Push translation files one by one for lang in push_languages: local_lang = lang if lang in list(lang_map.values()): remote_lang = lang_map.flip[lang] else: remote_lang = lang local_file = files[local_lang] kwargs = { 'lang': remote_lang, 'stats': stats, 'local_file': local_file, 'force': force, } if not self._should_push_translation(**kwargs): msg = "Skipping '%s' translation (file: %s)." logger.info(msg % (color_text(lang, "RED"), local_file)) continue msg = "Pushing '%s' translations (file: %s)" logger.warning( msg % (color_text(remote_lang, "RED"), local_file) ) try: self.do_url_request( 'push_translation', multipart=True, method='PUT', files=[( "%s;%s" % (resource_slug, remote_lang), self.get_full_path(local_file) )], language=remote_lang ) logger.debug("Translation %s pushed." % remote_lang) except HttpNotFound: if not source: logger.error("Resource hasn't been created. Try pushing source file.") except Exception as e: if isinstance(e, SSLError) or not skip: raise else: logger.error(e)
def cmd_init(argv, path_to_tx): """Initialize a new transifex project.""" parser = init_parser() (options, args) = parser.parse_args(argv) if len(args) > 1: parser.error("Too many arguments were provided. Aborting...") if args: path_to_tx = args[0] else: path_to_tx = os.getcwd() if os.path.isdir(os.path.join(path_to_tx, ".tx")): logger.info("tx: There is already a tx folder!") reinit = input("Do you want to delete it and " "reinit the project? [y/N]: ") while (reinit != 'y' and reinit != 'Y' and reinit != 'N' and reinit != 'n' and reinit != ''): reinit = input("Do you want to delete it and " "reinit the project? [y/N]: ") if not reinit or reinit in ['N', 'n', 'NO', 'no', 'No']: return # Clean the old settings # FIXME: take a backup else: rm_dir = os.path.join(path_to_tx, ".tx") shutil.rmtree(rm_dir) logger.info("Creating .tx folder...") os.mkdir(os.path.join(path_to_tx, ".tx")) # Handle the credentials through transifexrc home = os.path.expanduser("~") txrc = os.path.join(home, ".transifexrc") config = OrderedRawConfigParser() default_transifex = "https://www.transifex.com" transifex_host = options.host or input("Transifex instance [%s]: " % default_transifex) if not transifex_host: transifex_host = default_transifex if not transifex_host.startswith(('http://', 'https://')): transifex_host = 'https://' + transifex_host config_file = os.path.join(path_to_tx, ".tx", "config") if not os.path.exists(config_file): # The path to the config file (.tx/config) logger.info("Creating skeleton...") config = OrderedRawConfigParser() config.add_section('main') config.set('main', 'host', transifex_host) # Touch the file if it doesn't exist logger.info("Creating config file...") fh = open(config_file, 'w') config.write(fh) fh.close() prj = project.Project(path_to_tx) prj.getset_host_credentials(transifex_host, user=options.user, password=options.password) prj.save() logger.info("Done.")
def pull(self, languages=[], resources=[], overwrite=True, fetchall=False, fetchsource=False, force=False, skip=False, minimum_perc=0, mode=None, pseudo=False): """Pull all translations file from transifex server.""" self.minimum_perc = minimum_perc resource_list = self.get_chosen_resources(resources) if mode == 'reviewed': url = 'pull_reviewed_file' elif mode == 'translator': url = 'pull_translator_file' elif mode == 'developer': url = 'pull_developer_file' else: url = 'pull_file' for resource in resource_list: logger.debug("Handling resource %s" % resource) self.resource = resource project_slug, resource_slug = resource.split('.', 1) files = self.get_resource_files(resource) slang = self.get_resource_option(resource, 'source_lang') sfile = self.get_source_file(resource) lang_map = self.get_resource_lang_mapping(resource) host = self.get_resource_host(resource) logger.debug("Language mapping is: %s" % lang_map) if mode is None: mode = self._get_option(resource, 'mode') self.url_info = { 'host': host, 'project': project_slug, 'resource': resource_slug } logger.debug("URL data are: %s" % self.url_info) stats = self._get_stats_for_resource() try: file_filter = self.config.get(resource, 'file_filter') except configparser.NoOptionError: file_filter = None # Pull source file pull_languages = set([]) new_translations = set([]) if pseudo: pseudo_file = self._get_pseudo_file( slang, resource, file_filter ) if self._should_download(slang, stats, local_file=pseudo_file): logger.info("Pulling pseudo file for resource %s (%s)." % ( resource, color_text(pseudo_file, "RED") )) self._download_pseudo( project_slug, resource_slug, pseudo_file ) if not languages: continue if fetchall: new_translations = self._new_translations_to_add( files, slang, lang_map, stats, force ) if new_translations: msg = "New translations found for the following languages: %s" logger.info(msg % ', '.join(new_translations)) existing, new = self._languages_to_pull( languages, files, lang_map, stats, force ) pull_languages |= existing new_translations |= new logger.debug("Adding to new translations: %s" % new) if fetchsource: if sfile and slang not in pull_languages: pull_languages.add(slang) elif slang not in new_translations: new_translations.add(slang) if pull_languages: logger.debug("Pulling languages for: %s" % pull_languages) msg = "Pulling translations for resource %s (source: %s)" logger.info(msg % (resource, sfile)) for lang in pull_languages: local_lang = lang if lang in list(lang_map.values()): remote_lang = lang_map.flip[lang] else: remote_lang = lang if languages and lang not in pull_languages: logger.debug("Skipping language %s" % lang) continue if lang != slang: local_file = files.get(lang, None) or files[lang_map[lang]] else: local_file = sfile logger.debug("Using file %s" % local_file) kwargs = { 'lang': remote_lang, 'stats': stats, 'local_file': local_file, 'force': force, 'mode': mode, } if not self._should_update_translation(**kwargs): msg = "Skipping '%s' translation (file: %s)." logger.info( msg % (color_text(remote_lang, "RED"), local_file) ) continue if not overwrite: local_file = ("%s.new" % local_file) logger.warning( " -> %s: %s" % (color_text(remote_lang, "RED"), local_file) ) try: r, charset = self.do_url_request(url, language=remote_lang) except Exception as e: if isinstance(e, SSLError) or not skip: raise else: logger.error(e) continue base_dir = os.path.split(local_file)[0] mkdir_p(base_dir) fd = open(local_file, 'wb') fd.write(r.encode(charset)) fd.close() if new_translations: msg = "Pulling new translations for resource %s (source: %s)" logger.info(msg % (resource, sfile)) for lang in new_translations: if lang in list(lang_map.keys()): local_lang = lang_map[lang] else: local_lang = lang remote_lang = lang if file_filter: local_file = os.path.relpath( os.path.join( self.root, native_path( file_filter.replace('<lang>', local_lang) ) ), os.curdir ) else: trans_dir = os.path.join(self.root, ".tx", resource) if not os.path.exists(trans_dir): os.mkdir(trans_dir) local_file = os.path.relpath(os.path.join(trans_dir, '%s_translation' % local_lang, os.curdir)) if lang != slang: satisfies_min = self._satisfies_min_translated( stats[remote_lang], mode ) if not satisfies_min: msg = "Skipping language %s due to used options." logger.info(msg % lang) continue logger.warning( " -> %s: %s" % (color_text(remote_lang, "RED"), local_file) ) r, charset = self.do_url_request(url, language=remote_lang) base_dir = os.path.split(local_file)[0] mkdir_p(base_dir) fd = open(local_file, 'wb') fd.write(r.encode(charset)) fd.close()