示例#1
0
def project(userauth, prjname, lngname):
    pmd = config.cache.get_pmd(prjname)
    if pmd is None:
        abort(404, "Project does not exist")
        return

    pdata = pmd.pdata
    lng = pdata.languages.get(lngname)
    if lng is None:
        abort(404, "Language does not exist in the project")
        return
    assert pdata.projtype.allow_case or lng.case == [""]

    blng = pdata.get_base_language(
    )  # As above we established there is at least one language, this should work.
    stored = [[] for i in range(data.MAX_STATE)]
    sdict = pdata.statistics
    sdict = sdict.get(lngname)  # Statistics dict for the queried language.
    if sdict is None:
        abort(404, "Missing language statistics")
        return

    for sname, bchgs in blng.changes.items():
        cstates = sdict[sname]
        state = max(s[1] for s in cstates)
        if state != data.MISSING_OK:
            bchg = data.get_newest_change(bchgs, "")
            sdd = StringDisplayData(sname, bchg.base_text)
            chgs = lng.changes.get(sname)
            if chgs is not None:
                cases = data.get_all_newest_changes(chgs, lng.case)
                for case, cstate in cstates:
                    chg = cases[case]
                    if chg is not None:
                        if lng is blng:
                            text = chg.base_text.text
                        else:
                            text = chg.new_text.text
                            if text == "" and case != "":
                                # Suppress empty non-default case from translations.
                                continue
                        cdd = CaseDisplayData(case,
                                              data.STATE_MAP[cstate].name,
                                              text)
                        sdd.cases.append(cdd)

            stored[state].append(sdd)

    for strs in stored:
        strs.sort()
    return template("translation",
                    userauth=userauth,
                    pmd=pmd,
                    is_blng=(lng == blng),
                    lng=lng,
                    stored=stored)
示例#2
0
def handle_upload(userauth, pmd, projname, langfile, override, is_base,
                  lng_data):
    """
    Process the upload.

    @param userauth: User authentication.
    @type  userauth: L{UserAuthentication}

    @param pmd: Project meta data.
    @type  pmd: L{ProjectMetaData}

    @param projname: Project name.
    @type  projname: C{str}

    @param langfile: Language file to load if available.
    @type  langfile: C{file} or C{None}

    @param override: Override existing text.
    @type  override: C{bool}

    @param lng_data: Used language, if provided.
    @type  lng_data: L{LanguageData} or C{None}
    """
    pdata = pmd.pdata

    # Missing language file in the upload.
    if not langfile or not langfile.file:
        abort(404, "Missing language file")
        return

    base_language = pdata.get_base_language()

    # Cannot download a translation without base language.
    if not is_base and base_language is None:
        abort(404, "Project has no base language")
        return

    # Parse language file, and report any errors.
    ng_data = language_file.load_language_file(pdata.projtype, langfile.file,
                                               config.cfg.language_file_size,
                                               lng_data)
    if len(ng_data.errors) > 0:
        return template('upload_errors',
                        userauth=userauth,
                        pmd=pmd,
                        errors=ng_data.errors)

    # Is the language allowed?
    if not pdata.projtype.allow_language(ng_data.language_data):
        abort(
            404, "Language \"{}\" may not be uploaded".format(
                ng_data.language_data.isocode))
        return

    stamp = data.make_stamp()

    lng = pdata.languages.get(ng_data.language_data.isocode)
    if lng is None:  # New language being added.
        result = add_new_language(ng_data, pdata, is_base)
        if not result[0]:
            abort(404, result[0])
            return
        lng = result[1]
        if is_base and base_language is None: base_language = lng

    if is_base:
        if base_language is not None and base_language != lng:
            abort(404, "Cannot change a translation to a base language")
            return

        # Add strings as changes.
        for sv in ng_data.strings:
            sv.text = language_file.sanitize_text(sv.text)
            chgs = base_language.changes.get(sv.name)
            chg = get_blng_change(sv, base_language)
            if chg is None:  # New change.
                base_text = data.Text(sv.text, sv.case, stamp)
                chg = data.Change(sv.name, sv.case, base_text, None, stamp,
                                  userauth.name, True)
                if chgs is None:
                    chgs = [chg]
                    base_language.changes[sv.name] = chgs
                else:
                    chgs.append(chg)
            else:
                # Only way to update a base language is by upload, no need for override check.
                chg.stamp = stamp
                chg.user = userauth.name

            for c in chgs:
                c.last_upload = (c == chg)

        # Update language properties as well.
        copy_lng_properties(pdata.projtype, ng_data, base_language)

        pdata.skeleton = ng_data.skeleton  # Use the new skeleton file.
        pdata.flush_related_cache()  # Drop the related strings cache.
        pdata.set_modified()

        # Push the new set of string-names to all languages (this includes the base language).
        str_names = set(sv.name for sv in ng_data.strings)
        for lang in pdata.languages.values():
            lng_modified = False
            not_seen = str_names.copy()
            for sn in list(lang.changes.keys()):
                not_seen.discard(sn)
                if sn in str_names: continue  # Name is kept.
                del lang.changes[sn]  # Old string, delete
                lng_modified = True
            for sn in not_seen:
                # Missing translation are not saved, so no set_modified here.
                lang.changes[sn] = []

            if lng_modified:
                lang.set_modified()

    else:
        # Not a base language -> it is a translation.
        if base_language is not None and base_language == lng:
            abort(404, "Cannot change a base language to a translation")
            return

        for sv in ng_data.strings:
            sv.text = language_file.sanitize_text(sv.text)

            # Find base language string for 'sv'.
            bchgs = base_language.changes.get(sv.name)
            if bchgs is None:
                continue  # Translation has a string not in the base language
            bchg = data.get_newest_change(bchgs, '')
            if bchg is None: continue  # Nothing to base against.
            base_text = bchg.base_text

            chgs = lng.changes.get(sv.name)
            chg = get_lng_change(sv, lng, base_text)
            if chg is None:  # It's a new text or new case.
                lng_text = data.Text(sv.text, sv.case, stamp)
                chg = data.Change(sv.name, sv.case, base_text, lng_text, stamp,
                                  userauth.name, True)
                if chgs is None:
                    lng.changes[sv.name] = [chg]
                else:
                    for c in chgs:
                        c.last_upload = False
                    chgs.append(chg)
            elif override:  # Override existing entry.
                chg.stamp = stamp
                chg.user = userauth.name
                for c in chgs:
                    c.last_upload = (c == chg)

        # Update language properties as well.
        copy_lng_properties(pdata.projtype, ng_data, lng)
        lng.set_modified()

    config.cache.save_pmd(pmd)

    if is_base:
        pmd.create_statistics(None)  # Update all languages.
    else:
        pmd.create_statistics(lng)

    message = "Successfully uploaded language '" + lng.name + "' " + utils.get_datetime_now_formatted(
    )
    redirect("/project/<prjname>", prjname=projname, message=message)
示例#3
0
def output_string_edit_page(userauth,
                            bchg,
                            binfo,
                            lng,
                            pmd,
                            lngname,
                            sname,
                            states=None,
                            messages=[]):
    """
    Construct a page for editing a string.

    @param bchg: Last version of the base language.
    @type  bchg: L{Change}

    @param binfo: Information about the state of the L{bchg} string.
    @type  binfo: L{StringInfo}

    @param lng: Language being translated.
    @type  lng: L{Language}

    @param pmd: Project Meta Data.
    @type  pmd: L{ProjectMetaData}

    @param lngname: Language name.
    @type  lngname: C{str}


    @param sname: Name of the string.
    @type  sname: C{str}

    @param states: Changes, state and errors for (some of) the cases, omission of a case
                   means the function should derive it from the language.
    @type  states: C{dict} of C{str} to tuple (L{Change}, C{int}, C{list} of L{ErrorMessage}),
                   use C{None} to derive all.

    @return: Either an error, or an instantiated template.
    @rtype:  C{None} or C{str}
    """
    if states is None:
        states = {}
    pdata = pmd.pdata
    projtype = pdata.projtype

    # Mapping of case to list of related strings.
    related_cases = dict((case, []) for case in lng.case)
    for rel_sname in pdata.get_related_strings(sname):
        rel_chgs = lng.changes.get(rel_sname)
        if rel_chgs is not None:
            rel_chgs = data.get_all_newest_changes(rel_chgs, lng.case)
            for case, chg in rel_chgs.items():
                if chg is not None and chg.new_text is not None:
                    rc = related_cases.get(case)
                    if rc is not None:
                        rc.append(RelatedString(rel_sname, chg.new_text))

    case_chgs = data.get_all_changes(lng.changes.get(sname), lng.case, None)
    now = data.make_stamp()

    transl_cases = []
    for case in lng.case:
        tranls = []

        cchgs = [(cchg, True) for cchg in case_chgs[case]
                 ]  # Tuples (case-change, 'saved translation')
        chg_err_state = states.get(case)
        if chg_err_state is not None:
            cchgs.append((chg_err_state[0], False))
        if len(cchgs) == 0:
            # No changes for this case, make a dummy one to display the base data.
            tra = Translation(bchg, None, now, False)
            if case == "":
                tra.errors = [
                    language_file.ErrorMessage(language_file.ERROR, None,
                                               "String is missing")
                ]
                tra.state = data.STATE_MAP[data.MISSING].name
            else:
                tra.errors = []
                tra.state = data.STATE_MAP[data.MISSING_OK].name

            tranls.append(tra)

        else:
            # Changes do exist, add them (in reverse chronological order).
            cchgs.reverse()
            for idx, (lchg, saved) in enumerate(cchgs):
                tra = Translation(bchg, lchg, now, saved)
                if idx == 0:
                    # Newest string, add errors
                    if chg_err_state is not None:
                        state, errors = chg_err_state[1], chg_err_state[2]
                    else:
                        state, errors = data.get_string_status(
                            projtype, lchg, case, lng, bchg.base_text, binfo)

                    tra.errors = errors
                    tra.state = data.STATE_MAP[state].name
                else:
                    # For older translations, the errors and state are never displayed.
                    tra.errors = []
                    tra.state = data.STATE_MAP[data.MISSING_OK].name

                tranls.append(tra)

        if projtype.allow_case or case == "":
            transl_cases.append(
                TransLationCase(case, tranls, related_cases[case]))

    related_languages = []
    if lng.name[:3] != pdata.base_language[:3]:
        for n, l in pdata.languages.items():
            if n[:3] != lng.name[:3] or n == lng.name:
                continue

            related = data.get_newest_change(lng.changes.get(sname), "")
            if related is not None:
                related_languages.append((l, related))
    related_languages.sort(key=lambda x: x[0].name)

    return template(
        "string_form",
        userauth=userauth,
        pmd=pmd,
        lng=lng,
        sname=sname,
        plurals=language_info.all_plurals[lng.plural].description,
        genders=lng.gender,
        cases=lng.case,
        related_languages=related_languages,
        tcs=transl_cases,
        messages=messages,
    )


@route("/string/<prjname>/<lngname>/<sname>", method="GET")
@protected(["string", "prjname", "lngname"])
def str_form(userauth, prjname, lngname, sname):
    parms = check_page_parameters(prjname, lngname, sname)
    if parms is None:
        return

    pmd, bchg, lng, binfo = parms
    return output_string_edit_page(userauth, bchg, binfo, lng, pmd, lngname,
                                   sname, None)


@route("/string/<prjname>/<lngname>/<sname>", method="POST")
@protected(["string", "prjname", "lngname"])
def str_post(userauth, prjname, lngname, sname):
    parms = check_page_parameters(prjname, lngname, sname)
    if parms is None:
        return

    pmd, bchg, lng, binfo = parms

    request.forms.recode_unicode = False  # Allow Unicode input
    request_forms = request.forms.decode()  # Convert dict to Unicode.

    base_str = request_forms.get(
        "base")  # Base text translated against in the form.
    if base_str is None or base_str != bchg.base_text.text:
        abort(
            404,
            "Base language has been changed, please translate the newer version instead"
        )
        return

    # Get changes against bchg
    case_chgs = data.get_all_changes(lng.changes.get(sname), lng.case, bchg)
    projtype = pmd.pdata.projtype

    stamp = None  # Assigned a stamp object when a change is made in the translation.

    # Collected output data
    new_changes = []
    new_state_errors = {}

    for case in lng.case:
        trl_str = request_forms.get("text_" +
                                    case)  # Translation text in the form.
        if trl_str is None:
            continue  # It's missing from the form data.

        trl_str = language_file.sanitize_text(trl_str)
        if case == "" and trl_str == "" and bchg.base_text != "":
            # Empty base case for a non-empty string, was the "allow empty base translation" flag set?
            if request_forms.get("allow_empty_default") != "on":
                if stamp is None:
                    stamp = data.make_stamp()
                txt = data.Text(trl_str, case, stamp)
                tchg = data.Change(sname, case, bchg.base_text, txt, stamp,
                                   userauth.name)
                error = language_file.ErrorMessage(
                    language_file.ERROR,
                    None,
                    "Empty default case is not allowed (enable by setting 'Allow empty input').",
                )
                new_state_errors[case] = (tchg, data.INVALID, [error])
                continue

        # Check whether there is a match with a change in the translation.
        trl_chg = None
        for cchg in case_chgs[case]:
            if cchg.new_text.text == trl_str:
                trl_chg = cchg
                break

        if trl_chg is None:
            if case != "" and trl_str == "" and len(case_chgs[case]) == 0:
                continue  # Skip adding empty non-base cases if there are no strings.

            # A new translation against bchg!
            if stamp is None:
                stamp = data.make_stamp()
            txt = data.Text(trl_str, case, stamp)
            tchg = data.Change(sname, case, bchg.base_text, txt, stamp,
                               userauth.name)
            state, errors = data.get_string_status(projtype, tchg, case, lng,
                                                   bchg.base_text, binfo)
            if state == data.MISSING or state == data.INVALID:
                new_state_errors[case] = (tchg, state, errors)
            else:
                new_changes.append(tchg)

            continue
示例#4
0
文件: config.py 项目: frosch123/eints
    def create_statistics(self, parm_lng = None):
        """
        Construct overview statistics of the project.

        @param parm_lng: If specified only update the provided language. Otherwise, update all translations.
        @type  parm_lng: C{Language} or C{None}
        """
        pdata = self.pdata

        blng = pdata.get_base_language()
        if blng is None: return

        self.blang_name = blng.name
        self.blang_count = len(blng.changes)

        if parm_lng is None or parm_lng is blng: # Update all languages.
            pdata.statistics = {}

        # Get the pdata.statistics[lname] map for the base language.
        bstat = pdata.statistics.get(pdata.base_language)
        if bstat is None:
            bstat = {}
            pdata.statistics[pdata.base_language] = bstat

        projtype = pdata.projtype

        # First construct detailed information in the project
        for sname, bchgs in blng.changes.items():
            # Check newest base language string.
            bchg = data.get_newest_change(bchgs, '')
            binfo = language_file.check_string(projtype, bchg.base_text.text, True, None, blng, True)
            if binfo.has_error:
                bstat[sname] = [('', data.INVALID)]
            else:
                bstat[sname] = [('', data.UP_TO_DATE)]

            if parm_lng is None or parm_lng is blng: # Update all languages.
                lngs = pdata.languages.items()
            else:
                lngs = [(parm_lng.name, parm_lng)] # Update just 'parm_lng'

            for lname, lng in lngs:
                assert projtype.allow_case or lng.case == ['']
                if lng is blng: continue
                # Get the pdata.statistics[lname][sname] list.
                lstat = pdata.statistics.get(lname)
                if lstat is None:
                    lstat = {}
                    pdata.statistics[lname] = lstat
                sstat = lstat.get(sname)
                if sstat is None:
                    sstat = []
                    lstat[sname] = sstat

                if binfo is None: # Base string is broken, cannot judge translations.
                    sstat[:] = [('', data.UNKNOWN)]
                    continue

                chgs = lng.changes.get(sname)
                if chgs is None: # No translation at all
                    sstat[:] = [('', data.MISSING)]
                    continue

                chgs = data.get_all_newest_changes(chgs, lng.case)
                detailed_state = data.decide_all_string_status(projtype, bchg, chgs, lng, binfo)
                sstat[:] = sorted((c,se[0]) for c, se in detailed_state.items())

        # Construct overview statistics for each language.
        if parm_lng is None or parm_lng is blng: # Update all languages.
            lngs = pdata.languages.items()
            self.overview = {}
        else:
            lngs = [(parm_lng.name, parm_lng)] # Update just 'parm_lng'

        for lname, lng in lngs:
            #if lng is blng: continue
            counts = [ 0 for i in range(data.MAX_STATE) ]
            for sname in blng.changes:
                state = max(s[1] for s in pdata.statistics[lname][sname])
                if state != data.MISSING_OK: counts[state] = counts[state] + 1
            self.overview[lname] = counts