mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-13 10:47:21 +00:00
Added sharing a BeyondFacts Lab session with the research team (#1196)
This commit is contained in:
1 parent
a1b493cb13
commit
0885862150
27 files changed
+2852
-39
No files matched your search
@@ -102,6 +102,7 @@
|
||||
<MudSwitch T="bool" Value="@(project.HappenedAtUtc is not null)" ValueChanged="@this.SetHappenedAsync" Disabled="@this.isBusy" Color="Color.Primary" Class="mr-2">
|
||||
@T("The event has taken place")
|
||||
</MudSwitch>
|
||||
<MudButton StartIcon="@AppIcons.SHARE_WITH_RESEARCH" OnClick="@this.ShareAsync" Disabled="@this.isBusy">@T("Share with the research team")</MudButton>
|
||||
<MudButton StartIcon="@AppIcons.RENAME" OnClick="@this.RenameProjectAsync" Disabled="@this.isBusy">@T("Rename")</MudButton>
|
||||
<MudButton StartIcon="@AppIcons.DELETE" Color="Color.Error" OnClick="@this.DeleteProjectAsync" Disabled="@this.isBusy">@T("Delete")</MudButton>
|
||||
</MudStack>
|
||||
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
public partial class BeyondFactsLabAssistant
|
||||
{
|
||||
/// <summary>
|
||||
/// Opens the dialog with which the person shares the preparation shown with the research team of the lab.
|
||||
/// </summary>
|
||||
private Task ShareAsync() => this.ShowShareDialogAsync(false);
|
||||
|
||||
/// <summary>
|
||||
/// Offers to share the preparation once the person confirmed the work state of the last step for the first time,
|
||||
/// as the lab is done then; confirming it again later asks no more.
|
||||
/// </summary>
|
||||
/// <param name="projectId">The ID of the preparation.</param>
|
||||
private async Task OfferSharingAsync(Guid projectId)
|
||||
{
|
||||
try
|
||||
{
|
||||
var workState = await this.Store.LoadWorkStateAsync(projectId, BflStep.RESULTS);
|
||||
if (workState is not { LastRevision: 1, Confirmed: not null })
|
||||
return;
|
||||
}
|
||||
catch (InvalidDataException exception)
|
||||
{
|
||||
this.Logger.LogWarning(exception, "Could not read the work state of the last step of a preparation in the BeyondFacts Lab.");
|
||||
return;
|
||||
}
|
||||
|
||||
await this.ShowShareDialogAsync(true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows the dialog with which the person shares the preparation shown.
|
||||
/// </summary>
|
||||
/// <param name="afterCompletion">Whether the lab asks right after the last step, so that it thanks the person first.</param>
|
||||
private async Task ShowShareDialogAsync(bool afterCompletion)
|
||||
{
|
||||
if (this.selectedProject?.Project is not { } project)
|
||||
return;
|
||||
|
||||
var parameters = new DialogParameters<BflShareDialog>
|
||||
{
|
||||
{ dialog => dialog.ProjectId, project.ProjectId },
|
||||
{ dialog => dialog.AfterCompletion, afterCompletion },
|
||||
};
|
||||
|
||||
await this.DialogService.ShowAsync<BflShareDialog>(T("Share with the research team"), parameters, DialogOptions.FULLSCREEN);
|
||||
}
|
||||
}
|
||||
+8
-1
@@ -57,7 +57,8 @@ public partial class BeyondFactsLabAssistant
|
||||
|
||||
/// <summary>
|
||||
/// Starts the research once the person confirmed the event details, on every topic without one and on every topic
|
||||
/// whose public details changed since, and tells the person.
|
||||
/// whose public details changed since, and tells the person. Once the person confirmed the last step for the first
|
||||
/// time, the lab offers to share the preparation with its research team instead.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Nothing starts for an event which is not public, nor while the work state suggests whether the event is public
|
||||
@@ -67,6 +68,12 @@ public partial class BeyondFactsLabAssistant
|
||||
/// <param name="step">The step whose work state the person confirmed.</param>
|
||||
private async Task WorkStateConfirmedAsync(BflStep step)
|
||||
{
|
||||
if (step is BflStep.RESULTS && this.selectedProject?.Project is { } finishedProject)
|
||||
{
|
||||
await this.OfferSharingAsync(finishedProject.ProjectId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (step is not BflStep.FRAMEWORK || this.selectedProject?.Project is not { } project || this.ValidateProvider(this.providerSettings) is not null)
|
||||
return;
|
||||
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// A text after the anonymizer of the BeyondFacts Lab ran over it, with what it replaced.
|
||||
/// </summary>
|
||||
/// <param name="Text">The anonymized text.</param>
|
||||
/// <param name="Replacements">What the anonymizer replaced, in the order it first found each.</param>
|
||||
public sealed record BflAnonymization(string Text, IReadOnlyList<BflReplacement> Replacements)
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the result of a later run over the text, e.g., after the person edited it, together with what this run
|
||||
/// replaced already.
|
||||
/// </summary>
|
||||
/// <param name="later">The later run.</param>
|
||||
/// <returns>The text of the later run and the replacements of both runs, counted together.</returns>
|
||||
public BflAnonymization CombinedWith(BflAnonymization later)
|
||||
{
|
||||
var combined = this.Replacements.ToList();
|
||||
foreach (var replacement in later.Replacements)
|
||||
{
|
||||
var index = combined.FindIndex(candidate => candidate.Placeholder == replacement.Placeholder && candidate.Original == replacement.Original);
|
||||
if (index < 0)
|
||||
combined.Add(replacement);
|
||||
else
|
||||
combined[index] = combined[index] with { Count = combined[index].Count + replacement.Count };
|
||||
}
|
||||
|
||||
return new BflAnonymization(later.Text, combined);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,485 @@
|
||||
using System.Globalization;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Removes the personal details it recognizes from a session of the BeyondFacts Lab before the person shares it: links,
|
||||
/// e-mail addresses, paths, phone numbers, and the names and terms the person lists.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// The anonymizer follows rules and asks no model, so it recognizes only what its rules describe. Names count only when
|
||||
/// the person lists them; the lab suggests the ones it knows, see BflShareNames. A name is replaced in full, with its
|
||||
/// titles, in the genitive, with umlauts written out or left out, and in the forms of web addresses, such as
|
||||
/// "anna-mueller"; its first and its last name are replaced on their own as well, but only as they are written with a
|
||||
/// capital letter, since many names are common words. A part which several people share becomes a placeholder
|
||||
/// without a number.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// Running the anonymizer again over its own result changes nothing: it leaves its placeholders alone, and the same
|
||||
/// lists give the same numbers. That is why the person may edit the result and have it anonymized once more.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static partial class BflAnonymizer
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(BflAnonymizer).Namespace, nameof(BflAnonymizer));
|
||||
|
||||
/// <summary>
|
||||
/// The fewest digits a phone number has; a shorter number may be anything.
|
||||
/// </summary>
|
||||
private const int MIN_PHONE_DIGITS = 7;
|
||||
|
||||
/// <summary>
|
||||
/// The fewest letters a part of a name needs to be replaced on its own, so that an initial stays.
|
||||
/// </summary>
|
||||
private const int MIN_PART_LETTERS = 2;
|
||||
|
||||
/// <summary>
|
||||
/// The titles which may stand before a name, longer ones first.
|
||||
/// </summary>
|
||||
private const string TITLE = @"(?:Professorin|Professor|Prof|Dr\.?-Ing|Dipl\.?-Ing|Dr|PD|Herrn|Herr|Frau|Mrs|Mr|Ms|Mx|Sir|Dame)";
|
||||
|
||||
/// <summary>
|
||||
/// Any number of titles before a name, each followed by a period or a space, e.g., "Prof. Dr. ".
|
||||
/// </summary>
|
||||
private const string TITLES = $@"(?:{TITLE}(?:\.[ \t]*|[ \t]+))*";
|
||||
|
||||
/// <summary>
|
||||
/// What may stand between the parts of a name: spaces, or the characters with which web addresses join words.
|
||||
/// </summary>
|
||||
private const string PART_SEPARATOR = @"(?:\s+|[._-])?";
|
||||
|
||||
/// <summary>
|
||||
/// Where a name or a term may begin: not inside a word.
|
||||
/// </summary>
|
||||
private const string BEFORE = @"(?<![\p{L}\p{M}\p{N}_])";
|
||||
|
||||
/// <summary>
|
||||
/// Where a name or a term may end: not inside a word.
|
||||
/// </summary>
|
||||
private const string AFTER = @"(?![\p{L}\p{M}\p{N}_])";
|
||||
|
||||
/// <summary>
|
||||
/// Where a part of a name on its own may begin: neither inside a word nor after the hyphen of a double name.
|
||||
/// </summary>
|
||||
private const string BEFORE_PART = @"(?<![\p{L}\p{M}\p{N}_])(?<!\p{L}[-‐‑])";
|
||||
|
||||
/// <summary>
|
||||
/// Where a part of a name on its own may end: neither inside a word nor before the hyphen of a double name.
|
||||
/// </summary>
|
||||
private const string AFTER_PART = @"(?![\p{L}\p{M}\p{N}_]|[-‐‑]\p{L})";
|
||||
|
||||
/// <summary>
|
||||
/// The small words with which many last names begin, e.g., "von der Leyen".
|
||||
/// </summary>
|
||||
internal static readonly IReadOnlySet<string> NAME_PARTICLES = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"von", "vom", "van", "de", "der", "den", "del", "della", "di", "da", "du", "la", "le", "zu", "zum", "zur", "ten", "ter",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the personal details the anonymizer recognizes.
|
||||
/// </summary>
|
||||
/// <param name="text">The text, e.g., a session of the lab in Markdown.</param>
|
||||
/// <param name="people">The names of people to replace, one person each, e.g., "Prof. Dr. Anna Müller".</param>
|
||||
/// <param name="terms">Further terms to replace, e.g., an institution or a place.</param>
|
||||
/// <returns>The text with placeholders, and what was replaced.</returns>
|
||||
public static BflAnonymization Anonymize(string text, IReadOnlyList<string> people, IReadOnlyList<string> terms)
|
||||
{
|
||||
var report = new List<BflReplacement>();
|
||||
var link = Placeholder(TB("Link"));
|
||||
var email = Placeholder(TB("E-mail address"));
|
||||
var path = Placeholder(TB("Path"));
|
||||
var phone = Placeholder(TB("Phone number"));
|
||||
var result = text.Normalize(NormalizationForm.FormC);
|
||||
|
||||
// Links first, since an address may hold an e-mail address, a path, or a name:
|
||||
result = MarkdownLinkPattern().Replace(result, match =>
|
||||
{
|
||||
Record(report, link, match.Groups["url"].Value);
|
||||
var label = match.Groups["label"].Value.Trim();
|
||||
return label.Length > 0 ? $"{label} {link}" : link;
|
||||
});
|
||||
|
||||
result = AutoLinkPattern().Replace(result, match => Record(report, link, match.Groups["url"].Value));
|
||||
result = UrlPattern().Replace(result, match => ReplaceWithoutEnd(report, link, match.Value));
|
||||
result = EmailPattern().Replace(result, match => Record(report, email, match.Value));
|
||||
result = PathPattern().Replace(result, match => ReplaceWithoutEnd(report, path, match.Value));
|
||||
result = PhonePattern().Replace(result, match => IsPhoneNumber(match.Value) ? Record(report, phone, match.Value) : match.Value);
|
||||
result = ReplaceNamesAndTerms(result, people, terms, report, [link, email, path, phone]);
|
||||
return new BflAnonymization(result, report);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parts of a name as the person listed it: without titles and remarks in parentheses, and in the order
|
||||
/// first name, last name, also when the person wrote "Müller, Anna".
|
||||
/// </summary>
|
||||
/// <param name="name">The name.</param>
|
||||
/// <returns>The parts, empty when nothing like a name remains.</returns>
|
||||
internal static IReadOnlyList<string> PartsOfName(string name)
|
||||
{
|
||||
var cleaned = ParenthesesPattern().Replace(name.Normalize(NormalizationForm.FormC), " ").Replace("**", " ").Trim().Trim(',', ';', ':', '-', '–', '*', '_').Trim();
|
||||
var commaParts = cleaned.Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||
if (commaParts.Length > 1)
|
||||
{
|
||||
// "Müller, Anna" names the last name first; "Anna Müller, TUM" adds a remark:
|
||||
cleaned = commaParts.Length is 2 && !commaParts[0].Contains(' ') && !commaParts[1].Contains(' ') ? $"{commaParts[1]} {commaParts[0]}" : commaParts[0];
|
||||
}
|
||||
|
||||
var parts = cleaned.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries).Select(part => part.Trim(',', ';', ':')).Where(part => part.Any(char.IsLetter)).ToList();
|
||||
while (parts.Count > 0 && TitlePattern().IsMatch(parts[0]))
|
||||
parts.RemoveAt(0);
|
||||
|
||||
return parts;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tells whether a word is a title which may stand before a name, e.g., "Prof." or "Dr.".
|
||||
/// </summary>
|
||||
/// <param name="word">The word.</param>
|
||||
internal static bool IsTitle(string word) => TitlePattern().IsMatch(word);
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the names of people and the further terms.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <param name="people">The names of people, one person each.</param>
|
||||
/// <param name="terms">The further terms.</param>
|
||||
/// <param name="report">What was replaced so far.</param>
|
||||
/// <param name="fixedPlaceholders">The placeholders of links, e-mail addresses, paths, and phone numbers, which stay as they are.</param>
|
||||
/// <returns>The text with placeholders.</returns>
|
||||
private static string ReplaceNamesAndTerms(string text, IReadOnlyList<string> people, IReadOnlyList<string> terms, List<BflReplacement> report, IReadOnlyList<string> fixedPlaceholders)
|
||||
{
|
||||
var personLabel = TB("Person");
|
||||
var termLabel = TB("Term");
|
||||
var patterns = new List<BflNamePattern>();
|
||||
var singleParts = new Dictionary<string, string>(StringComparer.Ordinal);
|
||||
var ambiguous = Placeholder(personLabel);
|
||||
|
||||
var knownPeople = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
var number = 0;
|
||||
foreach (var name in people)
|
||||
{
|
||||
var parts = PartsOfName(name);
|
||||
if (parts.Count is 0 || !knownPeople.Add(string.Join(' ', parts)))
|
||||
continue;
|
||||
|
||||
var placeholder = Placeholder($"{personLabel} {++number}");
|
||||
if (parts.Count is 1)
|
||||
{
|
||||
AddSinglePart(singleParts, parts[0], placeholder, ambiguous);
|
||||
continue;
|
||||
}
|
||||
|
||||
var first = parts[0];
|
||||
var lastName = LastNameOf(parts);
|
||||
patterns.Add(new(TITLES + string.Join(PART_SEPARATOR, parts.Select(PartPattern)), string.Join(' ', parts).Length, placeholder, true));
|
||||
patterns.Add(new(PhrasePattern(lastName) + @"(?:,\s*|\s+|[._-])?" + PartPattern(first), lastName.Length + first.Length + 1, placeholder, true));
|
||||
if (parts.Count > 2)
|
||||
patterns.Add(new(TITLES + PartPattern(first) + PART_SEPARATOR + PhrasePattern(lastName), first.Length + lastName.Length + 1, placeholder, true));
|
||||
|
||||
AddSinglePart(singleParts, first, placeholder, ambiguous);
|
||||
AddSinglePart(singleParts, lastName, placeholder, ambiguous);
|
||||
if (lastName.Contains(' '))
|
||||
AddSinglePart(singleParts, parts[^1], placeholder, ambiguous);
|
||||
}
|
||||
|
||||
foreach (var (part, placeholder) in singleParts)
|
||||
patterns.Add(new(TITLES + PhrasePattern(part), part.Length, placeholder, false));
|
||||
|
||||
var knownTerms = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
|
||||
number = 0;
|
||||
foreach (var term in terms)
|
||||
{
|
||||
var parts = term.Normalize(NormalizationForm.FormC).Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries).Where(part => part.Any(char.IsLetterOrDigit)).ToList();
|
||||
if (parts.Count is 0 || !knownTerms.Add(string.Join(' ', parts)))
|
||||
continue;
|
||||
|
||||
patterns.Add(new(string.Join(PART_SEPARATOR, parts.Select(PartPattern)), string.Join(' ', parts).Length, Placeholder($"{termLabel} {++number}"), true));
|
||||
}
|
||||
|
||||
if (patterns.Count is 0)
|
||||
return text;
|
||||
|
||||
// The longest names first, so that "Anna Schmidt-Weber" is not taken for "Anna Schmidt":
|
||||
patterns = [.. patterns.OrderByDescending(pattern => pattern.Length)];
|
||||
var expression = new StringBuilder();
|
||||
expression.Append("(?<keep>");
|
||||
foreach (var placeholder in fixedPlaceholders)
|
||||
expression.Append(Regex.Escape(placeholder)).Append('|');
|
||||
|
||||
expression.Append(Regex.Escape($"[{personLabel}")).Append(@"(?: \d+)?\]|").Append(Regex.Escape($"[{termLabel}")).Append(@"(?: \d+)?\])");
|
||||
for (var index = 0; index < patterns.Count; index++)
|
||||
{
|
||||
//
|
||||
// A part of a name on its own must not be a part of a double name, e.g., "Schmidt" in "Müller-Schmidt",
|
||||
// which may be somebody else. A full name or a term may stand in a longer word with a hyphen, e.g., in
|
||||
// the address "anna-mueller-lab":
|
||||
//
|
||||
var pattern = patterns[index];
|
||||
var groupIndex = index.ToString(CultureInfo.InvariantCulture);
|
||||
expression.Append('|').Append(pattern.IgnoresCase ? BEFORE : BEFORE_PART);
|
||||
expression.Append("(?<v").Append(groupIndex).Append('>').Append(pattern.IgnoresCase ? "(?i:" : "(?:").Append(pattern.Pattern).Append("))");
|
||||
expression.Append("(?<s").Append(groupIndex).Append(">['’]s|['’]|s)?");
|
||||
expression.Append(pattern.IgnoresCase ? AFTER : AFTER_PART);
|
||||
}
|
||||
|
||||
var regex = new Regex(expression.ToString(), RegexOptions.CultureInvariant);
|
||||
var nameGroups = Enumerable.Range(0, patterns.Count).Select(index => regex.GroupNumberFromName($"v{index.ToString(CultureInfo.InvariantCulture)}")).ToArray();
|
||||
var suffixGroups = Enumerable.Range(0, patterns.Count).Select(index => regex.GroupNumberFromName($"s{index.ToString(CultureInfo.InvariantCulture)}")).ToArray();
|
||||
return regex.Replace(text, match =>
|
||||
{
|
||||
if (match.Groups["keep"].Success)
|
||||
return match.Value;
|
||||
|
||||
for (var index = 0; index < patterns.Count; index++)
|
||||
{
|
||||
// The genitive stays after the placeholder, e.g., "[Person 1]s":
|
||||
var group = match.Groups[nameGroups[index]];
|
||||
if (group.Success)
|
||||
return Record(report, patterns[index].Placeholder, group.Value) + match.Groups[suffixGroups[index]].Value;
|
||||
}
|
||||
|
||||
return match.Value;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last name of a name with several parts: from its first particle on, e.g., "von der Leyen", otherwise
|
||||
/// its last part.
|
||||
/// </summary>
|
||||
/// <param name="parts">The parts of the name, at least two.</param>
|
||||
private static string LastNameOf(IReadOnlyList<string> parts)
|
||||
{
|
||||
for (var index = 1; index < parts.Count - 1; index++)
|
||||
{
|
||||
if (NAME_PARTICLES.Contains(parts[index]))
|
||||
return string.Join(' ', parts.Skip(index));
|
||||
}
|
||||
|
||||
return parts[^1];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a part of a name which the anonymizer replaces on its own; a part several people share gets the
|
||||
/// placeholder without a number.
|
||||
/// </summary>
|
||||
/// <param name="singleParts">The parts so far, with their placeholders.</param>
|
||||
/// <param name="part">The part, e.g., a first name.</param>
|
||||
/// <param name="placeholder">The placeholder of the person.</param>
|
||||
/// <param name="ambiguous">The placeholder without a number.</param>
|
||||
private static void AddSinglePart(Dictionary<string, string> singleParts, string part, string placeholder, string ambiguous)
|
||||
{
|
||||
if (part.Count(char.IsLetter) < MIN_PART_LETTERS || NAME_PARTICLES.Contains(part))
|
||||
return;
|
||||
|
||||
singleParts[part] = singleParts.TryGetValue(part, out var known) && known != placeholder ? ambiguous : placeholder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pattern of several parts of a name which stand one after the other, e.g., "von der Leyen".
|
||||
/// </summary>
|
||||
/// <param name="phrase">The parts, separated by spaces.</param>
|
||||
private static string PhrasePattern(string phrase) => string.Join(@"\s+", phrase.Split(' ').Select(PartPattern));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pattern of one part of a name: umlauts match their spellings with and without them, letters with
|
||||
/// accents also without them, and the period of an initial may be missing.
|
||||
/// </summary>
|
||||
/// <param name="part">The part of the name.</param>
|
||||
private static string PartPattern(string part)
|
||||
{
|
||||
var pattern = new StringBuilder();
|
||||
for (var index = 0; index < part.Length; index++)
|
||||
{
|
||||
var character = part[index];
|
||||
var next = index + 1 < part.Length ? part[index + 1] : '\0';
|
||||
if (next is 'e' or 'E' && character is 'a' or 'o' or 'u' or 'A' or 'O' or 'U')
|
||||
{
|
||||
// A name written as "Mueller" also stands for "Müller":
|
||||
pattern.Append("(?:").Append(Regex.Escape(part.Substring(index, 2))).Append('|').Append(UmlautOf(character)).Append(')');
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (character is 's' && next is 's')
|
||||
{
|
||||
pattern.Append("(?:ss|ß)");
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
|
||||
pattern.Append(character switch
|
||||
{
|
||||
'ä' => "(?:ä|ae|a)",
|
||||
'ö' => "(?:ö|oe|o)",
|
||||
'ü' => "(?:ü|ue|u)",
|
||||
'Ä' => "(?:Ä|Ae|AE|A)",
|
||||
'Ö' => "(?:Ö|Oe|OE|O)",
|
||||
'Ü' => "(?:Ü|Ue|UE|U)",
|
||||
'ß' => "(?:ß|ss)",
|
||||
// A double name may lose its hyphen in an address, e.g., "MuellerLuedenscheidt":
|
||||
'-' or '‐' or '‑' => "[-‐‑._]?",
|
||||
'\'' or '’' => "['’]",
|
||||
'.' => @"\.?",
|
||||
|
||||
_ => LetterPattern(character),
|
||||
});
|
||||
}
|
||||
|
||||
return pattern.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the umlaut of a vowel, e.g., "ü" for "u".
|
||||
/// </summary>
|
||||
/// <param name="vowel">The vowel a, o, or u, in either case.</param>
|
||||
private static string UmlautOf(char vowel) => vowel switch
|
||||
{
|
||||
'a' => "ä",
|
||||
'o' => "ö",
|
||||
'u' => "ü",
|
||||
'A' => "Ä",
|
||||
'O' => "Ö",
|
||||
|
||||
_ => "Ü",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets the pattern of a letter, which matches a letter with an accent also without it.
|
||||
/// </summary>
|
||||
/// <param name="character">The letter.</param>
|
||||
private static string LetterPattern(char character)
|
||||
{
|
||||
var decomposed = character.ToString().Normalize(NormalizationForm.FormD);
|
||||
if (decomposed.Length > 1 && decomposed.Skip(1).All(mark => CharUnicodeInfo.GetUnicodeCategory(mark) is UnicodeCategory.NonSpacingMark))
|
||||
return $"(?:{Regex.Escape(character.ToString())}|{Regex.Escape(decomposed[..1])})";
|
||||
|
||||
return Regex.Escape(character.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tells whether a run of digits is a phone number rather than a date, a time, or a decimal number.
|
||||
/// </summary>
|
||||
/// <param name="candidate">The run of digits, as the pattern found it.</param>
|
||||
private static bool IsPhoneNumber(string candidate) => candidate.Count(char.IsDigit) >= MIN_PHONE_DIGITS && !NotAPhoneNumberPattern().IsMatch(candidate);
|
||||
|
||||
/// <summary>
|
||||
/// Replaces an address or a path, but leaves the punctuation after it, which belongs to the sentence.
|
||||
/// </summary>
|
||||
/// <param name="report">What was replaced so far.</param>
|
||||
/// <param name="placeholder">The placeholder.</param>
|
||||
/// <param name="value">What the pattern found.</param>
|
||||
/// <returns>The placeholder and the punctuation after it.</returns>
|
||||
private static string ReplaceWithoutEnd(List<BflReplacement> report, string placeholder, string value)
|
||||
{
|
||||
var length = value.Length;
|
||||
while (length > 0)
|
||||
{
|
||||
var last = value[length - 1];
|
||||
var opened = value[..length].Count(character => character == OpeningOf(last));
|
||||
var closed = value[..length].Count(character => character == last);
|
||||
if (last is '.' or ',' or ';' or ':' or '!' or '?' or '*' or '_' or '\'' or '"' || last is ')' or ']' or '}' && closed > opened)
|
||||
length--;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
return length is 0 ? value : Record(report, placeholder, value[..length]) + value[length..];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the opening bracket of a closing one, or a character no text holds.
|
||||
/// </summary>
|
||||
/// <param name="closing">The closing bracket.</param>
|
||||
private static char OpeningOf(char closing) => closing switch
|
||||
{
|
||||
')' => '(',
|
||||
']' => '[',
|
||||
'}' => '{',
|
||||
|
||||
_ => '\0',
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Notes a replacement in the report.
|
||||
/// </summary>
|
||||
/// <param name="report">What was replaced so far.</param>
|
||||
/// <param name="placeholder">The placeholder.</param>
|
||||
/// <param name="original">What it replaces.</param>
|
||||
/// <returns>The placeholder.</returns>
|
||||
private static string Record(List<BflReplacement> report, string placeholder, string original)
|
||||
{
|
||||
var index = report.FindIndex(replacement => replacement.Placeholder == placeholder && replacement.Original == original);
|
||||
if (index < 0)
|
||||
report.Add(new BflReplacement(placeholder, original, 1));
|
||||
else
|
||||
report[index] = report[index] with { Count = report[index].Count + 1 };
|
||||
|
||||
return placeholder;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a placeholder, e.g., "[Person 1]".
|
||||
/// </summary>
|
||||
/// <param name="label">What it stands for.</param>
|
||||
private static string Placeholder(string label) => $"[{label}]";
|
||||
|
||||
/// <summary>
|
||||
/// A link in Markdown, whose text stays and whose address goes.
|
||||
/// </summary>
|
||||
[GeneratedRegex("""\[(?<label>[^\]\n]*)\]\(\s*<?(?<url>(?:[a-z][a-z0-9+.-]*:|www\.)[^\s<>()]*(?:\([^\s<>()]*\)[^\s<>()]*)*)>?(?:\s+"[^"\n]*")?\s*\)""", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex MarkdownLinkPattern();
|
||||
|
||||
/// <summary>
|
||||
/// An address in angle brackets, e.g., "<https://example.org>".
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"<(?<url>(?:https?|ftp|file|mailto):[^\s<>]+)>", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex AutoLinkPattern();
|
||||
|
||||
/// <summary>
|
||||
/// An address of a web page or a file, with its scheme or beginning with "www.".
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"(?<![\p{L}\p{N}@.])(?:(?:https?|ftp|file)://|www\.)[^\s<>""'`]+", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex UrlPattern();
|
||||
|
||||
/// <summary>
|
||||
/// An e-mail address.
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"(?<![\p{L}\p{N}._%+-])[\p{L}\p{N}._%+-]+@[\p{L}\p{N}-]+(?:\.[\p{L}\p{N}-]+)*\.\p{L}{2,}", RegexOptions.CultureInvariant)]
|
||||
private static partial Regex EmailPattern();
|
||||
|
||||
/// <summary>
|
||||
/// A path of a file or a folder: on Windows with its drive or its server, otherwise beginning with "/" or "~" and
|
||||
/// with at least two folders.
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"(?<![\p{L}\p{N}])[A-Za-z]:[\\/][^\s""<>|*?]*|\\\\[^\s\\""<>|*?]+(?:\\[^\s""<>|*?]*)+|%[A-Za-z_]+%\\[^\s""<>|*?]*|(?<![\p{L}\p{M}\p{N}/.:~\\\])_-])(?:(?:~|\$HOME)(?:/[\p{L}\p{M}\p{N}._@+-]+)+|(?:/[\p{L}\p{M}\p{N}._@+-]+){2,})/?", RegexOptions.CultureInvariant)]
|
||||
private static partial Regex PathPattern();
|
||||
|
||||
/// <summary>
|
||||
/// A run which may be a phone number: beginning with "+" or "0", with spaces, periods, slashes, hyphens, or
|
||||
/// parentheses between the digits; IsPhoneNumber decides.
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"(?<![\p{L}\p{N}+(])\(?(?:\+|0)\d[\d \t./()-]{5,}\d(?![\p{L}\p{N}])", RegexOptions.CultureInvariant)]
|
||||
private static partial Regex PhonePattern();
|
||||
|
||||
/// <summary>
|
||||
/// What looks like a phone number but is not: a date, a span of time, or a decimal number.
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"^\(?\d{1,2}\.\d{1,2}\.(?:\d{4}|\d{2})$|^\d{1,2}[.:]\d{2}\s*-\s*\d{1,2}[.:]\d{2}$|^0[.,]", RegexOptions.CultureInvariant)]
|
||||
private static partial Regex NotAPhoneNumberPattern();
|
||||
|
||||
/// <summary>
|
||||
/// A remark in parentheses after a name, e.g., "(TUM)".
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"\([^)]*\)", RegexOptions.CultureInvariant)]
|
||||
private static partial Regex ParenthesesPattern();
|
||||
|
||||
/// <summary>
|
||||
/// A title on its own, e.g., "Prof." or "Dr.".
|
||||
/// </summary>
|
||||
[GeneratedRegex($@"^{TITLE}\.?$", RegexOptions.CultureInvariant)]
|
||||
private static partial Regex TitlePattern();
|
||||
}
|
||||
@@ -91,12 +91,12 @@ public static class BflFieldCatalog
|
||||
// The topic of the contribution may be unpublished, so it never reaches the research:
|
||||
new(CONTRIBUTION_TOPIC, BflStep.FRAMEWORK, BflFieldKind.MULTI_LINE) { IsKeyDetail = true },
|
||||
|
||||
new(EVENT_DATE, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE) { IsSharedWithResearch = true },
|
||||
new(EVENT_LOCATION, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE) { IsSharedWithResearch = true },
|
||||
new(EVENT_DATE, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE) { IsSharedWithResearch = true, IdentifiesEvent = true },
|
||||
new(EVENT_LOCATION, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE) { IsSharedWithResearch = true, IdentifiesEvent = true },
|
||||
new(EVENT_DURATION, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE),
|
||||
new(CONTRIBUTION_DURATION, BflStep.FRAMEWORK, BflFieldKind.SINGLE_LINE),
|
||||
new(OTHER_PARTICIPANTS, BflStep.FRAMEWORK, BflFieldKind.MULTI_LINE) { IsSharedWithResearch = true, NameRole = BflNameRole.PARTICIPANTS },
|
||||
new(EVENT_LINK, BflStep.FRAMEWORK, BflFieldKind.LINK) { IsSharedWithResearch = true },
|
||||
new(EVENT_LINK, BflStep.FRAMEWORK, BflFieldKind.LINK) { IsSharedWithResearch = true, IdentifiesEvent = true },
|
||||
|
||||
new(CONTRIBUTION_SUMMARY, BflStep.KNOWLEDGE, BflFieldKind.MULTI_LINE) { IsKeyDetail = true },
|
||||
new(CORE_THESES, BflStep.KNOWLEDGE, BflFieldKind.MULTI_LINE),
|
||||
|
||||
@@ -28,6 +28,15 @@ public sealed record BflFieldDefinition(string Id, BflStep Step, BflFieldKind Ki
|
||||
/// </remarks>
|
||||
public bool IsSharedWithResearch { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the field pins down the event, such as its date, its place, or its web page, so that a session
|
||||
/// shared with the research team of the lab leaves it out.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The research team learns how the person worked with the lab; which event it was would tell who the person is.
|
||||
/// </remarks>
|
||||
public bool IdentifiesEvent { get; init; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the field holds names of people.
|
||||
/// </summary>
|
||||
|
||||
@@ -56,6 +56,11 @@
|
||||
</MudJustifiedText>
|
||||
</MudAlert>
|
||||
|
||||
<MudText Typo="Typo.h6">@T("Sharing with the research team")</MudText>
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-4">
|
||||
@TA("With 'Share with the research team' above your preparation, you can share your session with the research team of the lab, so that it can develop the lab further; the lab also asks once you confirm the work state of the last step for the first time. Sharing is voluntary. Before you share, AI Studio removes the personal details it recognizes, and you check the preview, since its rules may miss something. You export the file and send it with your own mail program, which shows your address.")
|
||||
</MudJustifiedText>
|
||||
|
||||
<MudText Typo="Typo.h6">@T("How to recognize good results")</MudText>
|
||||
<MudList T="string" ReadOnly="@true" Dense="@true" Class="mud-text-list mb-3">
|
||||
<MudListItem T="string" Icon="@AppIcons.CHECKLIST_ITEM" Style="@LIST_ITEM_STYLE">
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// One way in which a name or a term may appear in a text, which the anonymizer of the BeyondFacts Lab replaces.
|
||||
/// </summary>
|
||||
/// <param name="Pattern">The regular expression of this way, without the boundaries around it.</param>
|
||||
/// <param name="Length">The length of what it stands for, so that the longest names are tried first.</param>
|
||||
/// <param name="Placeholder">The placeholder which replaces it.</param>
|
||||
/// <param name="IgnoresCase">Whether it matches in any case, e.g., in an address of a web page.</param>
|
||||
internal sealed record BflNamePattern(string Pattern, int Length, string Placeholder, bool IgnoresCase);
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// What the anonymizer of the BeyondFacts Lab replaced in a text, and how often.
|
||||
/// </summary>
|
||||
/// <param name="Placeholder">The placeholder which stands in the text now, e.g., "[Person 1]".</param>
|
||||
/// <param name="Original">What stood there before, as it was written.</param>
|
||||
/// <param name="Count">How often it was replaced.</param>
|
||||
public sealed record BflReplacement(string Placeholder, string Original, int Count);
|
||||
@@ -139,10 +139,18 @@
|
||||
|
||||
@if (kind is BflResultKind.REFLECTION_FOLDER)
|
||||
{
|
||||
<MudStack Row="true" Spacing="3" Wrap="Wrap.Wrap" Class="mb-1">
|
||||
<MudSwitch T="bool" @bind-Value="@this.includePersonalDisclosures" Color="Color.Primary" Disabled="@this.IsBusy(kind)">@TA("Include personal disclosures")</MudSwitch>
|
||||
<MudSwitch T="bool" @bind-Value="@this.includeConversations" Color="Color.Primary" Disabled="@this.IsBusy(kind)">@TA("Add the conversations as an appendix")</MudSwitch>
|
||||
</MudStack>
|
||||
<MudTextSwitch @bind-Value="@this.includePersonalDisclosures"
|
||||
Color="Color.Primary"
|
||||
Disabled="@this.IsBusy(kind)"
|
||||
Label="@T("Include personal disclosures?")"
|
||||
LabelOn="@(string.Format(Culture, TA("Yes, the folder holds your personal involvement and, in the appendix, the conversation of '{0}'"), BflStep.VALUES_AND_PERSPECTIVES.Title))"
|
||||
LabelOff="@(string.Format(Culture, TA("No, the folder leaves out your personal involvement and, in the appendix, the conversation of '{0}'"), BflStep.VALUES_AND_PERSPECTIVES.Title))"/>
|
||||
<MudTextSwitch @bind-Value="@this.includeConversations"
|
||||
Color="Color.Primary"
|
||||
Disabled="@this.IsBusy(kind)"
|
||||
Label="@T("Add the conversations as an appendix?")"
|
||||
LabelOn="@T("Yes, the folder ends with the conversations of all steps")"
|
||||
LabelOff="@T("No, the folder holds no conversations")"/>
|
||||
}
|
||||
|
||||
<MudButton Variant="@(result.Draft is null && result.Confirmed is null ? Variant.Filled : Variant.Outlined)"
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
@inherits MSGComponentBase
|
||||
<MudDialog>
|
||||
<DialogContent>
|
||||
@if (this.AfterCompletion)
|
||||
{
|
||||
<MudText Typo="Typo.h5" Class="mb-2">@TA("Thank you for preparing with the lab!")</MudText>
|
||||
}
|
||||
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
@TA("Would you like to share your session with the research team of the BeyondFacts Lab, so that it can develop the lab further? Before you share it, AI Studio removes the personal details it recognizes: links, e-mail addresses, paths, phone numbers, and the names and terms below. It follows fixed rules and may miss something, so please check the preview before you send it.")
|
||||
</MudJustifiedText>
|
||||
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
|
||||
@TA("The file holds your details, your confirmed work states, your conversations, and the materials you confirmed. It never holds the name of your preparation, your material, the research on the web, or the date, the place, and the web page of the event.")
|
||||
</MudJustifiedText>
|
||||
|
||||
@* Placeholders until the TUM provides its notice on data protection, like the address in BflShareMail: this notice, and in the next one, the promise on how the research institution stores the data. *@
|
||||
<MudAlert Severity="Severity.Info" Variant="Variant.Outlined" Icon="@AppIcons.PRIVACY" Dense="true" Class="mb-2">
|
||||
@TA("For research purposes, your data will be evaluated and analyzed in pseudonymized form.")
|
||||
</MudAlert>
|
||||
<MudAlert Severity="Severity.Warning" Variant="Variant.Outlined" Icon="@AppIcons.EMAIL" Dense="true" Class="mb-4">
|
||||
@TA("Your mail program sends the mail from your own address, so the research team sees who sends it. AI Studio anonymizes only the content of the file. The research institution stores your pseudonymized data so that nobody can link it to you or to your sender address.")
|
||||
</MudAlert>
|
||||
|
||||
@if (this.isLoading)
|
||||
{
|
||||
<MudProgressLinear Color="Color.Primary" Indeterminate="true" Class="mb-3"/>
|
||||
}
|
||||
else if (this.source is null)
|
||||
{
|
||||
<MudAlert Severity="Severity.Error" Variant="Variant.Outlined" Class="mb-3">
|
||||
@TA("AI Studio cannot open this preparation. Its files may be damaged, or a newer version of AI Studio stored them. AI Studio leaves the files unchanged.")
|
||||
</MudAlert>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="border-dashed border rounded-lg pa-3 mb-4">
|
||||
<MudText Typo="Typo.h6" Class="mb-1">@T("What AI Studio removes")</MudText>
|
||||
<MudJustifiedText Typo="Typo.body2" Class="mb-2">
|
||||
@TA("Names count only when they stand here. The lab suggests the names of other participants it knows; check them, and add your own name and everything else which points to you or to the event.")
|
||||
</MudJustifiedText>
|
||||
<MudTextField T="string" @bind-Value="@this.ownName" Label="@TA("Your name")" Variant="Variant.Outlined" Class="mb-3"/>
|
||||
<MudTextField T="string" @bind-Value="@this.otherNames" Label="@T("Names of other people, one per line")" Variant="Variant.Outlined" Lines="4" Sizing="InputSizing.Auto" MaxLines="12" Class="mb-3"/>
|
||||
<MudTextField T="string" @bind-Value="@this.terms" Label="@T("Further terms, one per line")" HelperText="@TA("E.g., your institution, the name of the event, or a place.")" Variant="Variant.Outlined" Lines="3" Sizing="InputSizing.Auto" MaxLines="12" Class="mb-3"/>
|
||||
<MudTextSwitch @bind-Value="@this.includePersonalDisclosures"
|
||||
Color="Color.Primary"
|
||||
Label="@T("Include personal disclosures?")"
|
||||
LabelOn="@(string.Format(Culture, TA("Yes, the file holds your personal involvement and the conversation of '{0}'"), BflStep.VALUES_AND_PERSPECTIVES.Title))"
|
||||
LabelOff="@(string.Format(Culture, TA("No, the file leaves out your personal involvement and the conversation of '{0}'"), BflStep.VALUES_AND_PERSPECTIVES.Title))"/>
|
||||
<MudButton Variant="@(this.preview is null ? Variant.Filled : Variant.Outlined)" Color="Color.Primary" StartIcon="@AppIcons.PREVIEW" OnClick="@this.CreatePreviewAsync">
|
||||
@(this.preview is null ? T("Create preview") : T("Create preview anew"))
|
||||
</MudButton>
|
||||
</div>
|
||||
|
||||
@if (this.preview is { } anonymization)
|
||||
{
|
||||
<MudText Typo="Typo.h6" Class="mb-1">@T("Preview")</MudText>
|
||||
<MudJustifiedText Typo="Typo.body2" Class="mb-2">
|
||||
@TA("Check the preview and change it where needed. AI Studio anonymizes your changes once more when you leave the field.")
|
||||
</MudJustifiedText>
|
||||
<MudTextField T="string"
|
||||
Value="@anonymization.Text"
|
||||
ValueChanged="@this.PreviewChanged"
|
||||
Label="@T("Session to share")"
|
||||
Variant="Variant.Outlined"
|
||||
Lines="16"
|
||||
Sizing="InputSizing.Auto"
|
||||
MaxLines="30"
|
||||
Class="mb-3"/>
|
||||
|
||||
<MudExpansionPanels Class="mb-4">
|
||||
<MudExpansionPanel Class="border-solid border rounded-lg">
|
||||
<TitleContent>
|
||||
<div class="d-flex align-center">
|
||||
<MudIcon Icon="@AppIcons.ANONYMIZED" Class="mr-3"/>
|
||||
<MudText Typo="Typo.subtitle1">@string.Format(Culture, T("What AI Studio replaced ({0})"), anonymization.Replacements.Sum(replacement => replacement.Count))</MudText>
|
||||
</div>
|
||||
</TitleContent>
|
||||
<ChildContent>
|
||||
@if (anonymization.Replacements.Count is 0)
|
||||
{
|
||||
<MudText Typo="Typo.body2">@T("Nothing was replaced.")</MudText>
|
||||
}
|
||||
else
|
||||
{
|
||||
<MudSimpleTable Dense="@true" Hover="@true">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>@T("Placeholder")</th>
|
||||
<th>@T("Replaced")</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach (var group in anonymization.Replacements.GroupBy(replacement => replacement.Placeholder))
|
||||
{
|
||||
<tr @key="group.Key">
|
||||
<td>@group.Key</td>
|
||||
<td>@string.Join(", ", group.Select(replacement => string.Format(Culture, T("{0} ({1}×)"), replacement.Original, replacement.Count)))</td>
|
||||
</tr>
|
||||
}
|
||||
</tbody>
|
||||
</MudSimpleTable>
|
||||
}
|
||||
</ChildContent>
|
||||
</MudExpansionPanel>
|
||||
</MudExpansionPanels>
|
||||
|
||||
<MudText Typo="Typo.h6" Class="mb-1">@T("Send it")</MudText>
|
||||
<MudJustifiedText Typo="Typo.body2" Class="mb-2">
|
||||
@TA("First, export the session as a file. Then prepare the mail to the research team, attach the file in your mail program, and send it.")
|
||||
</MudJustifiedText>
|
||||
<MudStack Row="true" Spacing="2" Wrap="Wrap.Wrap" Class="mb-2">
|
||||
<MudButton Variant="Variant.Filled" Color="Color.Primary" StartIcon="@AppIcons.EXPORT" OnClick="@this.ExportAsync" Disabled="@this.isExporting">
|
||||
@T("Create export")
|
||||
</MudButton>
|
||||
<MudButton Variant="Variant.Outlined" Color="Color.Primary" StartIcon="@AppIcons.EMAIL" Href="@this.MailUri" Target="_blank">
|
||||
@T("Prepare the mail to the research team")
|
||||
</MudButton>
|
||||
</MudStack>
|
||||
}
|
||||
}
|
||||
</DialogContent>
|
||||
<DialogActions>
|
||||
<MudButton OnClick="@this.Close" Variant="Variant.Filled">
|
||||
@(this.AfterCompletion && this.preview is null ? T("Not now") : T("Close"))
|
||||
</MudButton>
|
||||
</DialogActions>
|
||||
</MudDialog>
|
||||
@@ -0,0 +1,236 @@
|
||||
using System.Globalization;
|
||||
using System.Reflection;
|
||||
|
||||
using AIStudio.Components;
|
||||
using AIStudio.Dialogs;
|
||||
using AIStudio.Tools.Metadata;
|
||||
using AIStudio.Tools.Services;
|
||||
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
using DialogOptions = AIStudio.Dialogs.DialogOptions;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Lets the person share a session of the BeyondFacts Lab with its research team: anonymized, checked in a preview
|
||||
/// which the person may edit, exported as a file, and sent by mail from the mail program of the person.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Nothing of the dialog is stored. The lab suggests the names it knows each time, and the person enters their own
|
||||
/// name anew; the session itself stays as it is.
|
||||
/// </remarks>
|
||||
public partial class BflShareDialog : MSGComponentBase
|
||||
{
|
||||
[CascadingParameter]
|
||||
private IMudDialogInstance MudDialog { get; set; } = null!;
|
||||
|
||||
[Inject]
|
||||
private BeyondFactsLabStore Store { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private BflConversationService ConversationService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private RustService RustService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private AIGeneratedContentDisclosureService DisclosureService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private IDialogService DialogService { get; init; } = null!;
|
||||
|
||||
[Inject]
|
||||
private ILogger<BflShareDialog> Logger { get; init; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the ID of the preparation to share.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public Guid ProjectId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets whether the lab asks right after the person finished the last step, so that it thanks them first.
|
||||
/// </summary>
|
||||
[Parameter]
|
||||
public bool AfterCompletion { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The version of AI Studio, which goes into the session, since the prompts of the lab change with it.
|
||||
/// </summary>
|
||||
private static readonly string APP_VERSION = Assembly.GetExecutingAssembly().GetCustomAttribute<MetaDataAttribute>()?.Version ?? "unknown";
|
||||
|
||||
/// <summary>
|
||||
/// The format of every shared session, so that the research team gets all of them in the same one.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Markdown needs no Pandoc, so that everybody can share a session. The research team may ask for another format,
|
||||
/// but only one which AI Studio writes itself, see FileExportFormatExtensions.IsPlainText.
|
||||
/// </remarks>
|
||||
private const FileExportFormat EXPORT_FORMAT = FileExportFormat.MARKDOWN;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the culture of the active language plugin, for numbers.
|
||||
/// </summary>
|
||||
private static CultureInfo Culture => Tools.PluginSystem.I18N.I.Culture;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the link which opens the mail to the research team.
|
||||
/// </summary>
|
||||
private string MailUri { get; } = BflShareMail.CreateUri();
|
||||
|
||||
/// <summary>
|
||||
/// What the session is put together from, or null when the preparation cannot be opened.
|
||||
/// </summary>
|
||||
private BflShareInput? source;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the dialog still reads the preparation.
|
||||
/// </summary>
|
||||
private bool isLoading = true;
|
||||
|
||||
/// <summary>
|
||||
/// The name of the person, which the session should not contain.
|
||||
/// </summary>
|
||||
private string ownName = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// The names of other people, one per line, which the session should not contain.
|
||||
/// </summary>
|
||||
private string otherNames = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Further terms, one per line, which the session should not contain.
|
||||
/// </summary>
|
||||
private string terms = string.Empty;
|
||||
|
||||
/// <summary>
|
||||
/// Whether personal disclosures go into the session.
|
||||
/// </summary>
|
||||
private bool includePersonalDisclosures;
|
||||
|
||||
/// <summary>
|
||||
/// The anonymized session as the person sees it in the preview, with what was replaced; null before the first
|
||||
/// preview.
|
||||
/// </summary>
|
||||
private BflAnonymization? preview;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the person changed the preview, which a new preview would discard.
|
||||
/// </summary>
|
||||
private bool previewEdited;
|
||||
|
||||
/// <summary>
|
||||
/// Whether an export runs.
|
||||
/// </summary>
|
||||
private bool isExporting;
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await base.OnInitializedAsync();
|
||||
try
|
||||
{
|
||||
var forms = await this.Store.LoadFormsAsync(this.ProjectId);
|
||||
var workStates = await this.Store.LoadWorkStatesAsync(this.ProjectId);
|
||||
var results = await this.Store.LoadResultsAsync(this.ProjectId);
|
||||
var research = await this.Store.LoadAllResearchAsync(this.ProjectId);
|
||||
var materials = (await this.Store.ListMaterialsAsync(this.ProjectId)).Select(entry => entry.Material).OfType<BflMaterial>().ToList();
|
||||
var conversations = new Dictionary<BflStep, IReadOnlyList<BflConversationMessage>>();
|
||||
foreach (var step in BflStep.All)
|
||||
{
|
||||
if ((await this.ConversationService.GetAsync(new BflStepThreadLocation(this.ProjectId, step))).Thread is { } thread)
|
||||
conversations[step] = BflReflectionFolder.MessagesOf(thread);
|
||||
}
|
||||
|
||||
var researchTopics = research.Where(entry => !string.IsNullOrWhiteSpace(entry.Value.Text)).Select(entry => entry.Key).ToList();
|
||||
this.source = new BflShareInput(forms, workStates, results, conversations, materials, researchTopics, false, APP_VERSION, DateTimeOffset.UtcNow, Culture);
|
||||
this.otherNames = string.Join('\n', BflShareNames.Suggest(forms, workStates, research.GetValueOrDefault(BflResearchTopic.PARTICIPANTS)));
|
||||
}
|
||||
catch (Exception exception) when (exception is IOException or UnauthorizedAccessException or InvalidDataException)
|
||||
{
|
||||
this.Logger.LogError(exception, "Could not read the preparation '{ProjectId}' of the BeyondFacts Lab to share it.", this.ProjectId);
|
||||
this.source = null;
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.isLoading = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts the session together and anonymizes it with the names and terms the person listed; asks first when that
|
||||
/// would discard changes of the person in the preview.
|
||||
/// </summary>
|
||||
private async Task CreatePreviewAsync()
|
||||
{
|
||||
if (this.source is null)
|
||||
return;
|
||||
|
||||
if (this.previewEdited)
|
||||
{
|
||||
var parameters = new DialogParameters<ConfirmDialog>
|
||||
{
|
||||
{ dialog => dialog.Message, TA("Create the preview anew? Your changes in the preview will be lost.") },
|
||||
};
|
||||
|
||||
var reference = await this.DialogService.ShowAsync<ConfirmDialog>(T("Create preview anew"), parameters, DialogOptions.FULLSCREEN);
|
||||
var answer = await reference.Result;
|
||||
if (answer is null || answer.Canceled)
|
||||
return;
|
||||
}
|
||||
|
||||
var session = BflShareModelBuilder.Build(this.source with { IncludePersonalDisclosures = this.includePersonalDisclosures, SharedAtUtc = DateTimeOffset.UtcNow });
|
||||
this.preview = this.Anonymize(session);
|
||||
this.previewEdited = false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Anonymizes the preview once more after the person changed it, so that a name the person typed goes as well.
|
||||
/// </summary>
|
||||
/// <param name="text">The preview as the person changed it.</param>
|
||||
private void PreviewChanged(string? text)
|
||||
{
|
||||
if (this.preview is null)
|
||||
return;
|
||||
|
||||
this.preview = this.preview.CombinedWith(this.Anonymize(text ?? string.Empty));
|
||||
this.previewEdited = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exports the preview in the one format the research team gets, anonymized once more with the names and terms as
|
||||
/// they stand now, and with the notice that AI generated it when the person or the organization switched that on.
|
||||
/// </summary>
|
||||
private async Task ExportAsync()
|
||||
{
|
||||
if (this.preview is null)
|
||||
return;
|
||||
|
||||
this.preview = this.preview.CombinedWith(this.Anonymize(this.preview.Text));
|
||||
var fileName = $"{T("BeyondFacts Lab")} - {T("Shared session")}";
|
||||
this.isExporting = true;
|
||||
try
|
||||
{
|
||||
await PlainFileExport.ToFile(this.RustService, T("Share with the research team"), EXPORT_FORMAT, this.DisclosureService.AddDisclosureToMarkdown(this.preview.Text), fileName);
|
||||
}
|
||||
finally
|
||||
{
|
||||
this.isExporting = false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Anonymizes a text with the names and terms the person listed, the own name first.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
private BflAnonymization Anonymize(string text) => BflAnonymizer.Anonymize(text, [this.ownName, .. LinesOf(this.otherNames)], LinesOf(this.terms));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the entries of a field with one entry per line.
|
||||
/// </summary>
|
||||
/// <param name="text">The text of the field.</param>
|
||||
private static IReadOnlyList<string> LinesOf(string text) => text.Split('\n', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
private void Close() => this.MudDialog.Close(DialogResult.Ok(true));
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using System.Globalization;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// What a session shared with the research team of the BeyondFacts Lab is put together from.
|
||||
/// </summary>
|
||||
/// <param name="Forms">The forms of the preparation, by their steps.</param>
|
||||
/// <param name="WorkStates">The work states of the preparation, by their steps; a step which is missing has none.</param>
|
||||
/// <param name="Results">The results of the preparation, by their kinds; a kind which is missing has none.</param>
|
||||
/// <param name="Conversations">The conversations of the steps as the person saw them, by their steps.</param>
|
||||
/// <param name="Materials">The material of the preparation; only how much of each kind there is goes into the session.</param>
|
||||
/// <param name="ResearchTopics">The topics on which the lab researched the event on the web.</param>
|
||||
/// <param name="IncludePersonalDisclosures">Whether personal disclosures go into the session, e.g., how the topic touches the person.</param>
|
||||
/// <param name="AppVersion">The version of AI Studio, since the prompts of the lab change with it.</param>
|
||||
/// <param name="SharedAtUtc">When the person shares the session.</param>
|
||||
/// <param name="Culture">The culture for dates and numbers, the one of the language of the app.</param>
|
||||
public sealed record BflShareInput(IReadOnlyDictionary<BflStep, BflStepFormData> Forms, IReadOnlyDictionary<BflStep, BflWorkState> WorkStates, IReadOnlyDictionary<BflResultKind, BflResult> Results, IReadOnlyDictionary<BflStep, IReadOnlyList<BflConversationMessage>> Conversations, IReadOnlyList<BflMaterial> Materials, IReadOnlyCollection<BflResearchTopic> ResearchTopics, bool IncludePersonalDisclosures, string AppVersion, DateTimeOffset SharedAtUtc, CultureInfo Culture);
|
||||
@@ -0,0 +1,93 @@
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Prepares the mail with which a person sends a shared session to the research team of the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The lab writes no mail itself: a mailto link opens the mail program of the person with the address, the subject,
|
||||
/// and a short text, and the person attaches the exported file and sends it, from their own address. The link holds
|
||||
/// nothing of the session, since mail programs accept only short links; the runtime opens it outside the app.
|
||||
/// </remarks>
|
||||
public static class BflShareMail
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(BflShareMail).Namespace, nameof(BflShareMail));
|
||||
|
||||
/// <summary>
|
||||
/// The address of the research team.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A placeholder until the research team names its address; it is replaced before the release which brings the
|
||||
/// lab, as the lab is a prototype until then.
|
||||
/// </remarks>
|
||||
public const string RECIPIENT = "xxx@xxx.xxx";
|
||||
|
||||
/// <summary>
|
||||
/// The longest link the lab hands to a mail program. Some programs on Windows refuse longer ones.
|
||||
/// </summary>
|
||||
public const int MAX_URI_LENGTH = 1_800;
|
||||
|
||||
/// <summary>
|
||||
/// The line break of a mailto link, which RFC 6068 demands as CR LF.
|
||||
/// </summary>
|
||||
private const string LINE_BREAK = "\r\n";
|
||||
|
||||
/// <summary>
|
||||
/// Gets the subject of the mail, in the language of the app.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The mail speaks to the research team, not to the person, so neither the subject nor the text addresses anybody:
|
||||
/// the form of address the person chose for the lab does not fit the research team.
|
||||
/// </remarks>
|
||||
public static string Subject => TB("A shared session of the BeyondFacts Lab");
|
||||
|
||||
/// <summary>
|
||||
/// Gets the text of the mail, in the language of the app.
|
||||
/// </summary>
|
||||
public static string Body => TB("""
|
||||
Hello,
|
||||
|
||||
attached is a session of the BeyondFacts Lab, which I share for the research on the lab. Before I exported it, AI Studio removed the personal details it recognized.
|
||||
|
||||
Kind regards
|
||||
""");
|
||||
|
||||
/// <summary>
|
||||
/// Creates the link which opens the mail to the research team.
|
||||
/// </summary>
|
||||
public static string CreateUri() => CreateUri(RECIPIENT, Subject, Body);
|
||||
|
||||
/// <summary>
|
||||
/// Creates a mailto link, with everything encoded as RFC 6068 demands.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// A link longer than MAX_URI_LENGTH loses the end of its text, at a word, so that no mail program refuses it.
|
||||
/// </remarks>
|
||||
/// <param name="recipient">The address.</param>
|
||||
/// <param name="subject">The subject.</param>
|
||||
/// <param name="body">The text, with line breaks of any kind.</param>
|
||||
/// <returns>The link.</returns>
|
||||
public static string CreateUri(string recipient, string subject, string body)
|
||||
{
|
||||
var at = recipient.LastIndexOf('@');
|
||||
var address = at < 0 ? Uri.EscapeDataString(recipient) : $"{Uri.EscapeDataString(recipient[..at])}@{Uri.EscapeDataString(recipient[(at + 1)..])}";
|
||||
var prefix = $"mailto:{address}?subject={Uri.EscapeDataString(subject.ReplaceLineEndings(" "))}";
|
||||
|
||||
var text = body.Trim().ReplaceLineEndings("\n");
|
||||
var uri = Compose(prefix, text);
|
||||
while (uri.Length > MAX_URI_LENGTH && text.Length > 0)
|
||||
{
|
||||
var cut = text.LastIndexOfAny([' ', '\n']);
|
||||
text = cut > 0 ? text[..cut].TrimEnd() : string.Empty;
|
||||
uri = Compose(prefix, text);
|
||||
}
|
||||
|
||||
return uri;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Puts the link together from its beginning and its text.
|
||||
/// </summary>
|
||||
/// <param name="prefix">The link up to its subject.</param>
|
||||
/// <param name="text">The text, with LF line breaks.</param>
|
||||
private static string Compose(string prefix, string text) => text.Length is 0 ? prefix : $"{prefix}&body={Uri.EscapeDataString(text.Replace("\n", LINE_BREAK))}";
|
||||
}
|
||||
@@ -0,0 +1,217 @@
|
||||
using System.Text;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Puts a session of the BeyondFacts Lab together for its research team: how the person worked with the lab, step by
|
||||
/// step, without what would tell who the person is.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// <para>
|
||||
/// A session holds the details of every step, its confirmed work state, its conversation as the person saw it, and the
|
||||
/// confirmed materials the lab wrote. It leaves out everything the research team does not need and which would point
|
||||
/// to the person: the name of the preparation, the content and the file names of the material, the texts of the
|
||||
/// research on the web, which describe the event, the fields which pin down the event, drafts, and hidden messages. The
|
||||
/// reflection folder stays out as well, since it only repeats the confirmed work states. AI Studio keeps no profile
|
||||
/// and no path in a session, so none of them appears either. Personal disclosures stay out unless the person includes
|
||||
/// them, and so does the conversation of a step which may contain some.
|
||||
/// </para>
|
||||
/// <para>
|
||||
/// No model takes part, and the session is not anonymized yet: BflAnonymizer replaces the names and the other personal
|
||||
/// details in it afterward. The texts therefore stay as they were written, without Markdown escapes, which would hide
|
||||
/// an address or a phone number from the anonymizer. Every line ends with LF, on every system.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public static class BflShareModelBuilder
|
||||
{
|
||||
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(BflShareModelBuilder).Namespace, nameof(BflShareModelBuilder));
|
||||
|
||||
/// <summary>
|
||||
/// Puts the session together.
|
||||
/// </summary>
|
||||
/// <param name="input">What the session is put together from.</param>
|
||||
/// <returns>The session, in Markdown.</returns>
|
||||
public static string Build(BflShareInput input)
|
||||
{
|
||||
var text = new StringBuilder();
|
||||
text.Append("# ").AppendLineLF(TB("BeyondFacts Lab: a shared session"));
|
||||
AppendAbout(text, input);
|
||||
|
||||
foreach (var step in BflStep.All)
|
||||
AppendStep(text, step, input);
|
||||
|
||||
return text.ToString().TrimEnd() + "\n";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends what the research team needs to read the session: the version of AI Studio, the date, how much material
|
||||
/// the person added, and on which topics the lab researched.
|
||||
/// </summary>
|
||||
/// <param name="text">The session so far.</param>
|
||||
/// <param name="input">What the session is put together from.</param>
|
||||
private static void AppendAbout(StringBuilder text, BflShareInput input)
|
||||
{
|
||||
var documents = input.Materials.Count(material => material.Kind is BflMaterialKind.DOCUMENT);
|
||||
var recordings = input.Materials.Count(material => material.Kind is BflMaterialKind.RECORDING);
|
||||
var images = input.Materials.Count(material => material.Kind is BflMaterialKind.IMAGE);
|
||||
var topics = BflResearchTopic.All.Where(input.ResearchTopics.Contains).Select(topic => topic.Title).ToList();
|
||||
|
||||
text.AppendLineLF().Append("## ").AppendLineLF(TB("About this session")).AppendLineLF();
|
||||
AppendItem(text, TB("Version of AI Studio"), input.AppVersion);
|
||||
AppendItem(text, TB("Shared on"), input.SharedAtUtc.ToLocalTime().ToString("d", input.Culture));
|
||||
AppendItem(text, TB("Material"), string.Format(input.Culture, TB("{0} documents, {1} recordings, {2} images"), documents, recordings, images));
|
||||
AppendItem(text, TB("Research on the web"), topics.Count > 0 ? string.Join(", ", topics) : TB("none"));
|
||||
AppendItem(text, TB("Personal disclosures"), input.IncludePersonalDisclosures ? TB("included") : TB("left out"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a step: its details, its confirmed work state, its conversation, and for the last step the confirmed
|
||||
/// materials.
|
||||
/// </summary>
|
||||
/// <param name="text">The session so far.</param>
|
||||
/// <param name="step">The step.</param>
|
||||
/// <param name="input">What the session is put together from.</param>
|
||||
private static void AppendStep(StringBuilder text, BflStep step, BflShareInput input)
|
||||
{
|
||||
text.AppendLineLF().Append("## ").AppendLineLF(step.Title);
|
||||
AppendDetails(text, step, input);
|
||||
AppendWorkState(text, step, input);
|
||||
AppendConversation(text, step, input);
|
||||
if (step is BflStep.RESULTS)
|
||||
AppendResults(text, input);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the details of a step which the person entered, with the labels of the fields and options.
|
||||
/// </summary>
|
||||
/// <param name="text">The session so far.</param>
|
||||
/// <param name="step">The step.</param>
|
||||
/// <param name="input">What the session is put together from.</param>
|
||||
private static void AppendDetails(StringBuilder text, BflStep step, BflShareInput input)
|
||||
{
|
||||
text.AppendLineLF().Append("### ").AppendLineLF(TB("Details")).AppendLineLF();
|
||||
var form = input.Forms.GetValueOrDefault(step);
|
||||
var any = false;
|
||||
foreach (var field in BflFieldCatalog.ForStep(step))
|
||||
{
|
||||
if (form is null || field.IdentifiesEvent || field.IsPersonalDisclosure && !input.IncludePersonalDisclosures)
|
||||
continue;
|
||||
|
||||
var value = ValueOf(field, form);
|
||||
if (string.IsNullOrWhiteSpace(value))
|
||||
continue;
|
||||
|
||||
any = true;
|
||||
if (field.Kind is BflFieldKind.MULTI_LINE)
|
||||
text.Append("**").Append(field.Label).AppendLineLF(":**").AppendLineLF().AppendLineLF(WithLineBreaks(value)).AppendLineLF();
|
||||
else
|
||||
AppendItem(text, field.Label, value);
|
||||
}
|
||||
|
||||
if (!any)
|
||||
text.Append('_').Append(TB("No details.")).AppendLineLF("_");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the confirmed work state of a step, with all of its sections.
|
||||
/// </summary>
|
||||
/// <param name="text">The session so far.</param>
|
||||
/// <param name="step">The step.</param>
|
||||
/// <param name="input">What the session is put together from.</param>
|
||||
private static void AppendWorkState(StringBuilder text, BflStep step, BflShareInput input)
|
||||
{
|
||||
text.AppendLineLF().Append("### ").AppendLineLF(TB("Confirmed work state")).AppendLineLF();
|
||||
if (input.WorkStates.GetValueOrDefault(step)?.Confirmed is not { } confirmed)
|
||||
{
|
||||
text.Append('_').Append(TB("No confirmed work state.")).AppendLineLF("_");
|
||||
return;
|
||||
}
|
||||
|
||||
var any = false;
|
||||
foreach (var section in BflWorkStateSections.ForStep(step))
|
||||
{
|
||||
if (section.IsPersonalDisclosure && !input.IncludePersonalDisclosures || !confirmed.Sections.TryGetValue(section.Key, out var sectionText) || string.IsNullOrWhiteSpace(sectionText))
|
||||
continue;
|
||||
|
||||
any = true;
|
||||
text.Append("#### ").AppendLineLF(section.Title).AppendLineLF().AppendLineLF(WithLineBreaks(sectionText)).AppendLineLF();
|
||||
}
|
||||
|
||||
if (!any)
|
||||
text.Append('_').Append(TB("The confirmed work state holds nothing to share.")).AppendLineLF("_");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the conversation of a step as the person saw it.
|
||||
/// </summary>
|
||||
/// <param name="text">The session so far.</param>
|
||||
/// <param name="step">The step.</param>
|
||||
/// <param name="input">What the session is put together from.</param>
|
||||
private static void AppendConversation(StringBuilder text, BflStep step, BflShareInput input)
|
||||
{
|
||||
text.AppendLineLF().Append("### ").AppendLineLF(TB("Conversation")).AppendLineLF();
|
||||
if (!input.Conversations.TryGetValue(step, out var messages) || messages.Count is 0)
|
||||
{
|
||||
text.Append('_').Append(TB("No conversation.")).AppendLineLF("_");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!input.IncludePersonalDisclosures && BflReflectionFolder.MayHoldPersonalDisclosures(step))
|
||||
{
|
||||
text.Append('_').Append(TB("Left out, as it may contain personal disclosures.")).AppendLineLF("_");
|
||||
return;
|
||||
}
|
||||
|
||||
foreach (var message in messages)
|
||||
{
|
||||
var speaker = message.IsFromPerson ? TB("Person") : TB("Lab");
|
||||
text.Append("**").Append(speaker).AppendLineLF(":**").AppendLineLF().AppendLineLF(WithLineBreaks(message.Text)).AppendLineLF();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends the confirmed versions of the materials the lab wrote. The reflection folder stays out, since it only
|
||||
/// repeats the confirmed work states.
|
||||
/// </summary>
|
||||
/// <param name="text">The session so far.</param>
|
||||
/// <param name="input">What the session is put together from.</param>
|
||||
private static void AppendResults(StringBuilder text, BflShareInput input)
|
||||
{
|
||||
foreach (var kind in BflResultKind.All.Where(kind => kind.IsWrittenByModel))
|
||||
{
|
||||
text.AppendLineLF().Append("### ").AppendLineLF(kind.Title).AppendLineLF();
|
||||
if (input.Results.GetValueOrDefault(kind)?.Confirmed is { } confirmed && !string.IsNullOrWhiteSpace(confirmed.Text))
|
||||
text.AppendLineLF(WithLineBreaks(confirmed.Text));
|
||||
else
|
||||
text.Append('_').Append(TB("Not confirmed.")).AppendLineLF("_");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Appends a line of a list with a label in bold.
|
||||
/// </summary>
|
||||
/// <param name="text">The session so far.</param>
|
||||
/// <param name="label">The label.</param>
|
||||
/// <param name="value">The value, in one line.</param>
|
||||
private static void AppendItem(StringBuilder text, string label, string value) => text.Append("- **").Append(label).Append(":** ").AppendLineLF(value.Trim().ReplaceLineEndings(" "));
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value of a field as the person sees it: the labels of the options chosen, or the text entered.
|
||||
/// </summary>
|
||||
/// <param name="field">The field.</param>
|
||||
/// <param name="form">The form of the step of the field.</param>
|
||||
/// <returns>The value, empty when the field has none or only an option this version does not know.</returns>
|
||||
private static string ValueOf(BflFieldDefinition field, BflStepFormData form) => field.Kind switch
|
||||
{
|
||||
BflFieldKind.CHOICE => field.Options.FirstOrDefault(option => option.Id == form.Get(field.Id))?.Label ?? string.Empty,
|
||||
BflFieldKind.MULTI_CHOICE => string.Join(", ", field.Options.Where(option => form.GetChoices(field.Id).Contains(option.Id)).Select(option => option.Label)),
|
||||
|
||||
_ => form.Get(field.Id),
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Gets a text which the person or a model wrote, trimmed and with LF line breaks.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
private static string WithLineBreaks(string text) => text.Trim().ReplaceLineEndings("\n");
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Suggests the names of the other participants which a session shared with the research team of the BeyondFacts Lab
|
||||
/// should not contain, from what the lab knows about the event.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The lab knows names from three places: the field "Other participants", the headings of the research on the
|
||||
/// participants, which names each person in a heading of its own, and the names in bold in the section about the
|
||||
/// participants of a confirmed work state. A name is a run of two or three words with capital letters, or a single one
|
||||
/// when it stands alone; longer runs give their last two words. The rules miss names and find words which are none,
|
||||
/// so the person checks the suggestions and adds their own name.
|
||||
/// </remarks>
|
||||
public static partial class BflShareNames
|
||||
{
|
||||
/// <summary>
|
||||
/// The key of the section of a work state about the other participants, in the steps "Event details" and
|
||||
/// "Strategy".
|
||||
/// </summary>
|
||||
private const string PARTICIPANTS_SECTION = "PARTICIPANTS";
|
||||
|
||||
/// <summary>
|
||||
/// The most words a suggested name has.
|
||||
/// </summary>
|
||||
private const int MAX_NAME_WORDS = 3;
|
||||
|
||||
/// <summary>
|
||||
/// Words which start a run of capitals without belonging to a name: articles, and roles which often stand before a
|
||||
/// name, e.g., "Moderatorin Anna Müller".
|
||||
/// </summary>
|
||||
private static readonly IReadOnlySet<string> LEADING_WORDS = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
|
||||
{
|
||||
"Der", "Die", "Das", "Dem", "Den", "Des", "Ein", "Eine", "Einer", "The", "A", "An",
|
||||
"Moderation", "Moderator", "Moderatorin", "Gast", "Gäste", "Referent", "Referentin", "Podium", "Panel", "Keynote",
|
||||
"Speaker", "Host", "Chair", "Moderated", "Mit", "With",
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Suggests the names of the other participants.
|
||||
/// </summary>
|
||||
/// <param name="forms">The forms of the preparation, by their steps.</param>
|
||||
/// <param name="workStates">The work states of the preparation, by their steps.</param>
|
||||
/// <param name="participantsResearch">The research on the participants, or null when there is none.</param>
|
||||
/// <returns>The names, each once, in the order the lab found them.</returns>
|
||||
public static IReadOnlyList<string> Suggest(IReadOnlyDictionary<BflStep, BflStepFormData> forms, IReadOnlyDictionary<BflStep, BflWorkState> workStates, BflResearch? participantsResearch)
|
||||
{
|
||||
var candidates = new List<string>();
|
||||
foreach (var field in BflFieldCatalog.FIELDS.Where(field => field.NameRole is BflNameRole.PARTICIPANTS))
|
||||
{
|
||||
if (forms.TryGetValue(field.Step, out var form))
|
||||
candidates.AddRange(ListSeparatorPattern().Split(form.Get(field.Id)).SelectMany(NamesIn));
|
||||
}
|
||||
|
||||
if (participantsResearch is not null)
|
||||
candidates.AddRange(HeadingPattern().Matches(participantsResearch.Text).SelectMany(heading => NamesIn(heading.Groups["text"].Value)));
|
||||
|
||||
foreach (var step in BflStep.All)
|
||||
{
|
||||
if (workStates.GetValueOrDefault(step)?.Confirmed?.Sections.GetValueOrDefault(PARTICIPANTS_SECTION) is { } section)
|
||||
candidates.AddRange(BoldPattern().Matches(section).SelectMany(bold => NamesIn(bold.Groups["text"].Value)));
|
||||
}
|
||||
|
||||
return Distinct(candidates);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the names in a short text, e.g., an entry of a list or a heading.
|
||||
/// </summary>
|
||||
/// <param name="text">The text.</param>
|
||||
/// <returns>The names found.</returns>
|
||||
internal static IEnumerable<string> NamesIn(string text)
|
||||
{
|
||||
var cleaned = MarkdownLinkPattern().Replace(text, "${label}");
|
||||
cleaned = MarkupPattern().Replace(cleaned, " ");
|
||||
|
||||
// "Moderation: Anna Müller" and "Anna Müller – TUM" put the name and its role side by side:
|
||||
foreach (var segment in SegmentSeparatorPattern().Split(cleaned))
|
||||
{
|
||||
var words = segment.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries).Select(word => word.Trim(',', ';', '!', '?', '"', '„', '“', '”', '\'')).Where(word => word.Length > 0).ToList();
|
||||
if (words.Count is 1)
|
||||
{
|
||||
if (IsNameWord(words[0]) && !LEADING_WORDS.Contains(words[0]))
|
||||
yield return words[0];
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
var run = new List<string>();
|
||||
foreach (var word in words.Append(string.Empty))
|
||||
{
|
||||
// A particle continues a name, e.g., "Carla von der Heide", but never begins one:
|
||||
if (IsNameWord(word) || run.Count > 0 && BflAnonymizer.NAME_PARTICLES.Contains(word))
|
||||
{
|
||||
run.Add(word);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (NameOf(run) is { } name)
|
||||
yield return name;
|
||||
|
||||
run.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name a run of words with capital letters stands for, if any.
|
||||
/// </summary>
|
||||
/// <param name="run">The run, which may end with particles.</param>
|
||||
/// <returns>The name, or null when the run holds none.</returns>
|
||||
private static string? NameOf(List<string> run)
|
||||
{
|
||||
var words = run.SkipWhile(LEADING_WORDS.Contains).ToList();
|
||||
while (words.Count > 0 && BflAnonymizer.NAME_PARTICLES.Contains(words[^1]))
|
||||
words.RemoveAt(words.Count - 1);
|
||||
|
||||
if (words.Count < 2)
|
||||
return null;
|
||||
|
||||
// Particles do not count, so that "Carla von der Heide" stays whole:
|
||||
var significant = words.Count(word => !BflAnonymizer.NAME_PARTICLES.Contains(word));
|
||||
return string.Join(' ', significant > MAX_NAME_WORDS ? words.TakeLast(2) : words);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tells whether a word may be part of a name: it begins with a capital letter, has at least two letters or is an
|
||||
/// initial, holds nothing but letters, hyphens, and apostrophes, and is no title.
|
||||
/// </summary>
|
||||
/// <param name="word">The word.</param>
|
||||
private static bool IsNameWord(string word) => NameWordPattern().IsMatch(word) && !BflAnonymizer.IsTitle(word);
|
||||
|
||||
/// <summary>
|
||||
/// Removes names found twice, and a single word which is a part of a longer name found as well.
|
||||
/// </summary>
|
||||
/// <param name="candidates">The names, in the order found.</param>
|
||||
private static IReadOnlyList<string> Distinct(IReadOnlyList<string> candidates)
|
||||
{
|
||||
var parts = candidates.Where(candidate => candidate.Contains(' ')).SelectMany(candidate => candidate.Split(' ')).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
return [.. candidates.Where(candidate => candidate.Contains(' ') || !parts.Contains(candidate)).Distinct(StringComparer.OrdinalIgnoreCase)];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// What separates the entries of a list of participants: lines, semicolons, commas, slashes, and "and".
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"\r?\n|;|,|/|\s+(?:und|and|sowie|&)\s+", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex ListSeparatorPattern();
|
||||
|
||||
/// <summary>
|
||||
/// What separates a name from its role within an entry: a colon, a dash between spaces, or a remark in parentheses.
|
||||
/// </summary>
|
||||
[GeneratedRegex(@":|\s[-–—]\s|\([^)]*\)|\(|\)", RegexOptions.CultureInvariant)]
|
||||
private static partial Regex SegmentSeparatorPattern();
|
||||
|
||||
/// <summary>
|
||||
/// A heading of the third level, under which the research on the participants describes one person.
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"^[ \t]{0,3}###[ \t]+(?<text>[^\n]+?)[ \t#]*$", RegexOptions.Multiline | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex HeadingPattern();
|
||||
|
||||
/// <summary>
|
||||
/// A text in bold.
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"\*\*(?<text>[^*\n]+)\*\*|__(?<text>[^_\n]+)__", RegexOptions.CultureInvariant)]
|
||||
private static partial Regex BoldPattern();
|
||||
|
||||
/// <summary>
|
||||
/// A link in Markdown, whose text stays.
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"\[(?<label>[^\]\n]*)\]\([^)\n]*\)", RegexOptions.CultureInvariant)]
|
||||
private static partial Regex MarkdownLinkPattern();
|
||||
|
||||
/// <summary>
|
||||
/// The marks of Markdown which may surround a name: emphasis, code, and headings.
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"\*+|_{2,}|`+|^#+", RegexOptions.Multiline | RegexOptions.CultureInvariant)]
|
||||
private static partial Regex MarkupPattern();
|
||||
|
||||
/// <summary>
|
||||
/// A word which may be part of a name.
|
||||
/// </summary>
|
||||
[GeneratedRegex(@"^(?:\p{Lu}[\p{L}\p{M}'’-]*\p{L}|\p{Lu}\.)$", RegexOptions.CultureInvariant)]
|
||||
private static partial Regex NameWordPattern();
|
||||
}
|
||||
@@ -43,6 +43,7 @@ Für schnelle Texte wie eine Pressemitteilung passen der Chat von AI Studio oder
|
||||
- Mit „Gespräch beginnen“ eröffnet das Lab das Gespräch eines Schritts. Die Person kann auch selbst zuerst schreiben und sagen, wobei sie Unterstützung möchte, etwa bei ihrer Rolle, ihrer Strategie oder einer kritischen Fragerunde.
|
||||
- Unter dem Gespräch eines Schritts erzeugt das Lab auf Wunsch einen Arbeitsstand: eine Zusammenfassung dessen, was die Person in dem Schritt geklärt hat. Sie prüft, bearbeitet und bestätigt ihn; die späteren Schritte bauen auf den bestätigten Arbeitsständen auf. Dabei schlägt das Lab Werte für leere Felder in „Ihre Angaben“ vor, die die Person einzeln übernimmt.
|
||||
- Im letzten Schritt erstellt das Lab aus den bestätigten Arbeitsständen fünf Materialien: eine Reflexionsmappe, ein Positionsblatt, einen Gesprächsleitfaden, Fragen und Antwortansätze sowie offene Punkte und nächste Schritte. Die Person prüft, bearbeitet und bestätigt jedes davon und exportiert es als Dokument. Persönliche Angaben nimmt die Reflexionsmappe nur auf, wenn die Person es wählt.
|
||||
- Mit „Mit dem Forschungsteam teilen“ über einer Vorbereitung kann die Person ihre Sitzung mit dem Forschungsteam des Labs um Lara Wüster teilen, damit es das Lab weiterentwickelt; das Lab fragt auch von selbst, sobald sie den Arbeitsstand des letzten Schritts zum ersten Mal bestätigt. Das Teilen ist freiwillig. Vorher entfernt AI Studio nach festen Regeln die persönlichen Angaben, die es erkennt: Links, E-Mail-Adressen, Pfade, Telefonnummern sowie die Namen und Begriffe, die die Person einträgt. Die Regeln können etwas übersehen, deshalb prüft und bearbeitet die Person die Vorschau. Die Datei enthält nie den Namen der Vorbereitung, das Material, die Recherche sowie Termin, Ort und Link der Veranstaltung, persönliche Angaben nur, wenn die Person es wählt. Die Person exportiert die Datei und schickt sie mit ihrem eigenen E-Mail-Programm; die E-Mail zeigt ihre Absenderadresse. Die Forschungseinrichtung speichert die pseudonymisierten Daten so, dass kein Zusammenhang mit der Person und ihrer Absenderadresse möglich ist.
|
||||
- Die Schritte bauen aufeinander auf, die Person kann sie aber in beliebiger Reihenfolge bearbeiten.
|
||||
- Eine Vorbereitung dauert etwa 30 bis 60 Minuten. Die Person kann jederzeit unterbrechen: AI Studio speichert ihre Angaben und Gespräche, und sie macht später dort weiter.
|
||||
- Für jede Vorbereitung wählt die Person einen KI-Anbieter. Das Lab bietet nur Anbieter an, denen sie oder ihre Organisation genug vertraut, und das Modell muss mindestens 32.000 Token auf einmal lesen können.
|
||||
|
||||
@@ -24,4 +24,8 @@ Auf Wunsch erstellst du auch kleinere Hilfen:
|
||||
- Redestruktur: Zeitblöcke, Ziel je Abschnitt, Kernpunkte, Übergänge und Schluss.
|
||||
- Folienplan: eine Botschaft je Folie, mit einer Idee für die Darstellung und einer Notiz zum Sprechen.
|
||||
|
||||
Persönliche Angaben aus „Werte und Perspektiven“ nimmst du nur in ein Material auf, wenn die Person das ausdrücklich wünscht.
|
||||
Persönliche Angaben aus „Werte und Perspektiven“ nimmst du nur in ein Material auf, wenn die Person das ausdrücklich wünscht.
|
||||
|
||||
### Teilen mit dem Forschungsteam
|
||||
|
||||
Bestätigt die Person den Arbeitsstand dieses Schritts zum ersten Mal, fragt das Lab, ob sie ihre Sitzung mit dem Forschungsteam des Labs teilen möchte; später geht das über „Mit dem Forschungsteam teilen“ über der Vorbereitung. Das Teilen ist freiwillig; dränge die Person nicht dazu. Fragt sie danach, erkläre es so: AI Studio entfernt vorher nach festen Regeln die persönlichen Angaben, die es erkennt, kann aber etwas übersehen. Deshalb prüft sie die Vorschau, exportiert die Datei und schickt sie mit ihrem eigenen E-Mail-Programm, das ihre Absenderadresse zeigt. Die Forschungseinrichtung speichert die pseudonymisierten Daten so, dass kein Zusammenhang mit der Person und ihrer Absenderadresse möglich ist.
|
||||
@@ -769,6 +769,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::
|
||||
-- Cannot be opened
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1981873292"] = "Cannot be opened"
|
||||
|
||||
-- Share with the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2202378897"] = "Share with the research team"
|
||||
|
||||
-- Copy preparation ID
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2344012850"] = "Copy preparation ID"
|
||||
|
||||
@@ -904,6 +907,24 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::
|
||||
-- Permanently delete the preparation '{0}' with all of its steps, chats, material, and work states?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T98434423::INFORMAL"] = "Permanently delete the preparation '{0}' with all of its steps, chats, material, and work states?"
|
||||
|
||||
-- Link
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T2382947721"] = "Link"
|
||||
|
||||
-- E-mail address
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T2411920080"] = "E-mail address"
|
||||
|
||||
-- Person
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T3278826400"] = "Person"
|
||||
|
||||
-- Path
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T3949388886"] = "Path"
|
||||
|
||||
-- Term
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T586836727"] = "Term"
|
||||
|
||||
-- Phone number
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T987655160"] = "Phone number"
|
||||
|
||||
-- Stop
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T1266644741"] = "Stop"
|
||||
|
||||
@@ -1315,6 +1336,15 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2001857
|
||||
-- Work through the seven steps, in any order you like. Each step has the form 'Your details' and a conversation of its own.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2001857602::INFORMAL"] = "Work through the seven steps, in any order you like. Each step has the form 'Your details' and a conversation of its own."
|
||||
|
||||
-- With 'Share with the research team' above your preparation, you can share your session with the research team of the lab, so that it can develop the lab further; the lab also asks once you confirm the work state of the last step for the first time. Sharing is voluntary. Before you share, AI Studio removes the personal details it recognizes, and you check the preview, since its rules may miss something. You export the file and send it with your own mail program, which shows your address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2242659102"] = "With 'Share with the research team' above your preparation, you can share your session with the research team of the lab, so that it can develop the lab further; the lab also asks once you confirm the work state of the last step for the first time. Sharing is voluntary. Before you share, AI Studio removes the personal details it recognizes, and you check the preview, since its rules may miss something. You export the file and send it with your own mail program, which shows your address."
|
||||
|
||||
-- With 'Share with the research team' above your preparation, you can share your session with the research team of the lab, so that it can develop the lab further; the lab also asks once you confirm the work state of the last step for the first time. Sharing is voluntary. Before you share, AI Studio removes the personal details it recognizes, and you check the preview, since its rules may miss something. You export the file and send it with your own mail program, which shows your address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2242659102::INFORMAL"] = "With 'Share with the research team' above your preparation, you can share your session with the research team of the lab, so that it can develop the lab further; the lab also asks once you confirm the work state of the last step for the first time. Sharing is voluntary. Before you share, AI Studio removes the personal details it recognizes, and you check the preview, since its rules may miss something. You export the file and send it with your own mail program, which shows your address."
|
||||
|
||||
-- Sharing with the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2278322216"] = "Sharing with the research team"
|
||||
|
||||
-- How to recognize good results
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2310692629"] = "How to recognize good results"
|
||||
|
||||
@@ -1858,6 +1888,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T1597401023"]
|
||||
-- A work state changed since the lab wrote this draft. You can create it anew.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T1597401023::INFORMAL"] = "A work state changed since the lab wrote this draft. You can create it anew."
|
||||
|
||||
-- No, the folder holds no conversations
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T1599137736"] = "No, the folder holds no conversations"
|
||||
|
||||
-- The lab writes these materials from your confirmed work states. Check and edit each of them, and confirm it. Then you can export it as a document.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T182456751"] = "The lab writes these materials from your confirmed work states. Check and edit each of them, and confirm it. Then you can export it as a document."
|
||||
|
||||
@@ -1879,6 +1912,12 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2174778813"]
|
||||
-- These steps have no confirmed work state yet: {0}. The materials use only what you confirmed and name what is still open.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2174778813::INFORMAL"] = "These steps have no confirmed work state yet: {0}. The materials use only what you confirmed and name what is still open."
|
||||
|
||||
-- No, the folder leaves out your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T219251710"] = "No, the folder leaves out your personal involvement and, in the appendix, the conversation of '{0}'"
|
||||
|
||||
-- No, the folder leaves out your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T219251710::INFORMAL"] = "No, the folder leaves out your personal involvement and, in the appendix, the conversation of '{0}'"
|
||||
|
||||
-- Discard draft
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2273967454"] = "Discard draft"
|
||||
|
||||
@@ -1903,17 +1942,20 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2679230892"]
|
||||
-- Confirm
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2709768687"] = "Confirm"
|
||||
|
||||
-- Yes, the folder holds your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2791825400"] = "Yes, the folder holds your personal involvement and, in the appendix, the conversation of '{0}'"
|
||||
|
||||
-- Yes, the folder holds your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2791825400::INFORMAL"] = "Yes, the folder holds your personal involvement and, in the appendix, the conversation of '{0}'"
|
||||
|
||||
-- A work state changed since you confirmed this material. Please create it anew, or check whether it still fits.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2881263992"] = "A work state changed since you confirmed this material. Please create it anew, or check whether it still fits."
|
||||
|
||||
-- A work state changed since you confirmed this material. Please create it anew, or check whether it still fits.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2881263992::INFORMAL"] = "A work state changed since you confirmed this material. Please create it anew, or check whether it still fits."
|
||||
|
||||
-- Add the conversations as an appendix
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3185544181"] = "Add the conversations as an appendix"
|
||||
|
||||
-- Add the conversations as an appendix
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3185544181::INFORMAL"] = "Add the conversations as an appendix"
|
||||
-- Add the conversations as an appendix?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2968063742"] = "Add the conversations as an appendix?"
|
||||
|
||||
-- Being written
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3204128327"] = "Being written"
|
||||
@@ -1921,6 +1963,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3204128327"]
|
||||
-- Edit
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3267849393"] = "Edit"
|
||||
|
||||
-- Yes, the folder ends with the conversations of all steps
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3386507601"] = "Yes, the folder ends with the conversations of all steps"
|
||||
|
||||
-- AI Studio could not save this material. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3460571754"] = "AI Studio could not save this material. Please try again."
|
||||
|
||||
@@ -1936,11 +1981,8 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3898821075"]
|
||||
-- Create all materials in parallel
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3926195314"] = "Create all materials in parallel"
|
||||
|
||||
-- Include personal disclosures
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T4086721095"] = "Include personal disclosures"
|
||||
|
||||
-- Include personal disclosures
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T4086721095::INFORMAL"] = "Include personal disclosures"
|
||||
-- Include personal disclosures?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3989412584"] = "Include personal disclosures?"
|
||||
|
||||
-- Outdated
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T724820205"] = "Outdated"
|
||||
@@ -1951,6 +1993,225 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T934952176"]
|
||||
-- Confirmed
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T942512558"] = "Confirmed"
|
||||
|
||||
-- Replaced
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1120626693"] = "Replaced"
|
||||
|
||||
-- Names count only when they stand here. The lab suggests the names of other participants it knows; check them, and add your own name and everything else which points to you or to the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1161078151"] = "Names count only when they stand here. The lab suggests the names of other participants it knows; check them, and add your own name and everything else which points to you or to the event."
|
||||
|
||||
-- Names count only when they stand here. The lab suggests the names of other participants it knows; check them, and add your own name and everything else which points to you or to the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1161078151::INFORMAL"] = "Names count only when they stand here. The lab suggests the names of other participants it knows; check them, and add your own name and everything else which points to you or to the event."
|
||||
|
||||
-- Create preview
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1209353201"] = "Create preview"
|
||||
|
||||
-- Nothing was replaced.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1254778913"] = "Nothing was replaced."
|
||||
|
||||
-- Create export
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1293027637"] = "Create export"
|
||||
|
||||
-- Check the preview and change it where needed. AI Studio anonymizes your changes once more when you leave the field.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1446555152"] = "Check the preview and change it where needed. AI Studio anonymizes your changes once more when you leave the field."
|
||||
|
||||
-- Check the preview and change it where needed. AI Studio anonymizes your changes once more when you leave the field.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1446555152::INFORMAL"] = "Check the preview and change it where needed. AI Studio anonymizes your changes once more when you leave the field."
|
||||
|
||||
-- Shared session
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1494358904"] = "Shared session"
|
||||
|
||||
-- No, the file leaves out your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1859480999"] = "No, the file leaves out your personal involvement and the conversation of '{0}'"
|
||||
|
||||
-- No, the file leaves out your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1859480999::INFORMAL"] = "No, the file leaves out your personal involvement and the conversation of '{0}'"
|
||||
|
||||
-- What AI Studio replaced ({0})
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2050374220"] = "What AI Studio replaced ({0})"
|
||||
|
||||
-- Share with the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2202378897"] = "Share with the research team"
|
||||
|
||||
-- Yes, the file holds your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2300527633"] = "Yes, the file holds your personal involvement and the conversation of '{0}'"
|
||||
|
||||
-- Yes, the file holds your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2300527633::INFORMAL"] = "Yes, the file holds your personal involvement and the conversation of '{0}'"
|
||||
|
||||
-- Placeholder
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2307342650"] = "Placeholder"
|
||||
|
||||
-- Send it
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2402701656"] = "Send it"
|
||||
|
||||
-- Further terms, one per line
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T275020693"] = "Further terms, one per line"
|
||||
|
||||
-- What AI Studio removes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2839457006"] = "What AI Studio removes"
|
||||
|
||||
-- Create the preview anew? Your changes in the preview will be lost.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2839487925"] = "Create the preview anew? Your changes in the preview will be lost."
|
||||
|
||||
-- Create the preview anew? Your changes in the preview will be lost.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2839487925::INFORMAL"] = "Create the preview anew? Your changes in the preview will be lost."
|
||||
|
||||
-- AI Studio cannot open this preparation. Its files may be damaged, or a newer version of AI Studio stored them. AI Studio leaves the files unchanged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3058916397"] = "AI Studio cannot open this preparation. Its files may be damaged, or a newer version of AI Studio stored them. AI Studio leaves the files unchanged."
|
||||
|
||||
-- AI Studio cannot open this preparation. Its files may be damaged, or a newer version of AI Studio stored them. AI Studio leaves the files unchanged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3058916397::INFORMAL"] = "AI Studio cannot open this preparation. Its files may be damaged, or a newer version of AI Studio stored them. AI Studio leaves the files unchanged."
|
||||
|
||||
-- Prepare the mail to the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3134014858"] = "Prepare the mail to the research team"
|
||||
|
||||
-- For research purposes, your data will be evaluated and analyzed in pseudonymized form.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T343267095"] = "For research purposes, your data will be evaluated and analyzed in pseudonymized form."
|
||||
|
||||
-- For research purposes, your data will be evaluated and analyzed in pseudonymized form.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T343267095::INFORMAL"] = "For research purposes, your data will be evaluated and analyzed in pseudonymized form."
|
||||
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3448155331"] = "Close"
|
||||
|
||||
-- Names of other people, one per line
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3883857514"] = "Names of other people, one per line"
|
||||
|
||||
-- Session to share
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T390620119"] = "Session to share"
|
||||
|
||||
-- {0} ({1}×)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3914405868"] = "{0} ({1}×)"
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- E.g., your institution, the name of the event, or a place.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3960545771"] = "E.g., your institution, the name of the event, or a place."
|
||||
|
||||
-- E.g., your institution, the name of the event, or a place.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3960545771::INFORMAL"] = "E.g., your institution, the name of the event, or a place."
|
||||
|
||||
-- Include personal disclosures?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3989412584"] = "Include personal disclosures?"
|
||||
|
||||
-- Preview
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T4258942199"] = "Preview"
|
||||
|
||||
-- The file holds your details, your confirmed work states, your conversations, and the materials you confirmed. It never holds the name of your preparation, your material, the research on the web, or the date, the place, and the web page of the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T47760612"] = "The file holds your details, your confirmed work states, your conversations, and the materials you confirmed. It never holds the name of your preparation, your material, the research on the web, or the date, the place, and the web page of the event."
|
||||
|
||||
-- The file holds your details, your confirmed work states, your conversations, and the materials you confirmed. It never holds the name of your preparation, your material, the research on the web, or the date, the place, and the web page of the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T47760612::INFORMAL"] = "The file holds your details, your confirmed work states, your conversations, and the materials you confirmed. It never holds the name of your preparation, your material, the research on the web, or the date, the place, and the web page of the event."
|
||||
|
||||
-- Would you like to share your session with the research team of the BeyondFacts Lab, so that it can develop the lab further? Before you share it, AI Studio removes the personal details it recognizes: links, e-mail addresses, paths, phone numbers, and the names and terms below. It follows fixed rules and may miss something, so please check the preview before you send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T483133022"] = "Would you like to share your session with the research team of the BeyondFacts Lab, so that it can develop the lab further? Before you share it, AI Studio removes the personal details it recognizes: links, e-mail addresses, paths, phone numbers, and the names and terms below. It follows fixed rules and may miss something, so please check the preview before you send it."
|
||||
|
||||
-- Would you like to share your session with the research team of the BeyondFacts Lab, so that it can develop the lab further? Before you share it, AI Studio removes the personal details it recognizes: links, e-mail addresses, paths, phone numbers, and the names and terms below. It follows fixed rules and may miss something, so please check the preview before you send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T483133022::INFORMAL"] = "Would you like to share your session with the research team of the BeyondFacts Lab, so that it can develop the lab further? Before you share it, AI Studio removes the personal details it recognizes: links, e-mail addresses, paths, phone numbers, and the names and terms below. It follows fixed rules and may miss something, so please check the preview before you send it."
|
||||
|
||||
-- Thank you for preparing with the lab!
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T527414024"] = "Thank you for preparing with the lab!"
|
||||
|
||||
-- Thank you for preparing with the lab!
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T527414024::INFORMAL"] = "Thank you for preparing with the lab!"
|
||||
|
||||
-- First, export the session as a file. Then prepare the mail to the research team, attach the file in your mail program, and send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T667916114"] = "First, export the session as a file. Then prepare the mail to the research team, attach the file in your mail program, and send it."
|
||||
|
||||
-- First, export the session as a file. Then prepare the mail to the research team, attach the file in your mail program, and send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T667916114::INFORMAL"] = "First, export the session as a file. Then prepare the mail to the research team, attach the file in your mail program, and send it."
|
||||
|
||||
-- Not now
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T742223822"] = "Not now"
|
||||
|
||||
-- Your mail program sends the mail from your own address, so the research team sees who sends it. AI Studio anonymizes only the content of the file. The research institution stores your pseudonymized data so that nobody can link it to you or to your sender address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T892175069"] = "Your mail program sends the mail from your own address, so the research team sees who sends it. AI Studio anonymizes only the content of the file. The research institution stores your pseudonymized data so that nobody can link it to you or to your sender address."
|
||||
|
||||
-- Your mail program sends the mail from your own address, so the research team sees who sends it. AI Studio anonymizes only the content of the file. The research institution stores your pseudonymized data so that nobody can link it to you or to your sender address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T892175069::INFORMAL"] = "Your mail program sends the mail from your own address, so the research team sees who sends it. AI Studio anonymizes only the content of the file. The research institution stores your pseudonymized data so that nobody can link it to you or to your sender address."
|
||||
|
||||
-- Create preview anew
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T953794800"] = "Create preview anew"
|
||||
|
||||
-- Your name
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T973497277"] = "Your name"
|
||||
|
||||
-- Your name
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T973497277::INFORMAL"] = "Your name"
|
||||
|
||||
-- A shared session of the BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMAIL::T3284671944"] = "A shared session of the BeyondFacts Lab"
|
||||
|
||||
-- Hello, attached is a session of the BeyondFacts Lab, which I share for the research on the lab. Before I exported it, AI Studio removed the personal details it recognized. Kind regards
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMAIL::T3622173728"] = "Hello,\n\nattached is a session of the BeyondFacts Lab, which I share for the research on the lab. Before I exported it, AI Studio removed the personal details it recognized.\n\nKind regards"
|
||||
|
||||
-- No confirmed work state.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1085223273"] = "No confirmed work state."
|
||||
|
||||
-- No conversation.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1296316689"] = "No conversation."
|
||||
|
||||
-- Research on the web
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1322369504"] = "Research on the web"
|
||||
|
||||
-- Shared on
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1703957189"] = "Shared on"
|
||||
|
||||
-- Personal disclosures
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1899851975"] = "Personal disclosures"
|
||||
|
||||
-- left out
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1940843988"] = "left out"
|
||||
|
||||
-- Details
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T247611973"] = "Details"
|
||||
|
||||
-- Conversation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2640583206"] = "Conversation"
|
||||
|
||||
-- Not confirmed.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2652477427"] = "Not confirmed."
|
||||
|
||||
-- {0} documents, {1} recordings, {2} images
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2667668036"] = "{0} documents, {1} recordings, {2} images"
|
||||
|
||||
-- none
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2913447899"] = "none"
|
||||
|
||||
-- No details.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2977469358"] = "No details."
|
||||
|
||||
-- The confirmed work state holds nothing to share.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3153189308"] = "The confirmed work state holds nothing to share."
|
||||
|
||||
-- included
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3202028217"] = "included"
|
||||
|
||||
-- Person
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3278826400"] = "Person"
|
||||
|
||||
-- Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3303746196"] = "Lab"
|
||||
|
||||
-- Material
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3419754368"] = "Material"
|
||||
|
||||
-- Confirmed work state
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3444987166"] = "Confirmed work state"
|
||||
|
||||
-- About this session
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3460157970"] = "About this session"
|
||||
|
||||
-- BeyondFacts Lab: a shared session
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3731741448"] = "BeyondFacts Lab: a shared session"
|
||||
|
||||
-- Left out, as it may contain personal disclosures.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3986361996"] = "Left out, as it may contain personal disclosures."
|
||||
|
||||
-- Version of AI Studio
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T728223168"] = "Version of AI Studio"
|
||||
|
||||
-- Test situation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Test situation"
|
||||
|
||||
|
||||
+271
-10
@@ -771,6 +771,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::
|
||||
-- Cannot be opened
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1981873292"] = "Kann nicht geöffnet werden"
|
||||
|
||||
-- Share with the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2202378897"] = "Mit dem Forschungsteam teilen"
|
||||
|
||||
-- Copy preparation ID
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2344012850"] = "Vorbereitungs-ID kopieren"
|
||||
|
||||
@@ -906,6 +909,24 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::
|
||||
-- Permanently delete the preparation '{0}' with all of its steps, chats, material, and work states?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T98434423::INFORMAL"] = "Die Vorbereitung „{0}“ mit allen Schritten, Chats, Arbeitsständen und ihrem Material endgültig löschen?"
|
||||
|
||||
-- Link
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T2382947721"] = "Link"
|
||||
|
||||
-- E-mail address
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T2411920080"] = "E-Mail-Adresse"
|
||||
|
||||
-- Person
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T3278826400"] = "Person"
|
||||
|
||||
-- Path
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T3949388886"] = "Pfad"
|
||||
|
||||
-- Term
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T586836727"] = "Begriff"
|
||||
|
||||
-- Phone number
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T987655160"] = "Telefonnummer"
|
||||
|
||||
-- Stop
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T1266644741"] = "Stoppen"
|
||||
|
||||
@@ -1317,6 +1338,15 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2001857
|
||||
-- Work through the seven steps, in any order you like. Each step has the form 'Your details' and a conversation of its own.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2001857602::INFORMAL"] = "Bearbeite die sieben Schritte in beliebiger Reihenfolge. Jeder Schritt hat das Formular „Deine Angaben“ und ein eigenes Gespräch."
|
||||
|
||||
-- With 'Share with the research team' above your preparation, you can share your session with the research team of the lab, so that it can develop the lab further; the lab also asks once you confirm the work state of the last step for the first time. Sharing is voluntary. Before you share, AI Studio removes the personal details it recognizes, and you check the preview, since its rules may miss something. You export the file and send it with your own mail program, which shows your address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2242659102"] = "Mit „Mit dem Forschungsteam teilen“ über Ihrer Vorbereitung können Sie Ihre Sitzung mit dem Forschungsteam des Labs teilen, damit es das Lab weiterentwickeln kann. Das Lab fragt auch von selbst, sobald Sie den Arbeitsstand des letzten Schritts zum ersten Mal bestätigen. Das Teilen ist freiwillig. Vorher entfernt AI Studio die persönlichen Angaben, die es erkennt, und Sie prüfen die Vorschau, da seine Regeln etwas übersehen können. Sie exportieren die Datei und senden sie mit Ihrem eigenen E-Mail-Programm, das Ihre Adresse zeigt."
|
||||
|
||||
-- With 'Share with the research team' above your preparation, you can share your session with the research team of the lab, so that it can develop the lab further; the lab also asks once you confirm the work state of the last step for the first time. Sharing is voluntary. Before you share, AI Studio removes the personal details it recognizes, and you check the preview, since its rules may miss something. You export the file and send it with your own mail program, which shows your address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2242659102::INFORMAL"] = "Mit „Mit dem Forschungsteam teilen“ über deiner Vorbereitung kannst du deine Sitzung mit dem Forschungsteam des Labs teilen, damit es das Lab weiterentwickeln kann. Das Lab fragt auch von selbst, sobald du den Arbeitsstand des letzten Schritts zum ersten Mal bestätigst. Das Teilen ist freiwillig. Vorher entfernt AI Studio die persönlichen Angaben, die es erkennt, und du prüfst die Vorschau, da seine Regeln etwas übersehen können. Du exportierst die Datei und sendest sie mit deinem eigenen E-Mail-Programm, das deine Adresse zeigt."
|
||||
|
||||
-- Sharing with the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2278322216"] = "Teilen mit dem Forschungsteam"
|
||||
|
||||
-- How to recognize good results
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2310692629"] = "Gute Ergebnisse erkennen"
|
||||
|
||||
@@ -1860,6 +1890,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T1597401023"]
|
||||
-- A work state changed since the lab wrote this draft. You can create it anew.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T1597401023::INFORMAL"] = "Seit das Lab diesen Entwurf geschrieben hat, hat sich ein Arbeitsstand geändert. Du kannst ihn neu erstellen."
|
||||
|
||||
-- No, the folder holds no conversations
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T1599137736"] = "Nein, die Mappe enthält keine Gespräche"
|
||||
|
||||
-- The lab writes these materials from your confirmed work states. Check and edit each of them, and confirm it. Then you can export it as a document.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T182456751"] = "Das Lab schreibt diese Materialien aus Ihren bestätigten Arbeitsständen. Prüfen und bearbeiten Sie jedes davon, und bestätigen Sie es. Danach können Sie es als Dokument exportieren."
|
||||
|
||||
@@ -1881,6 +1914,12 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2174778813"]
|
||||
-- These steps have no confirmed work state yet: {0}. The materials use only what you confirmed and name what is still open.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2174778813::INFORMAL"] = "Diese Schritte haben noch keinen bestätigten Arbeitsstand: {0}. Die Materialien verwenden nur, was du bestätigt hast, und nennen, was noch offen ist."
|
||||
|
||||
-- No, the folder leaves out your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T219251710"] = "Nein, die Mappe lässt Ihre persönliche Betroffenheit weg und im Anhang das Gespräch von „{0}“"
|
||||
|
||||
-- No, the folder leaves out your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T219251710::INFORMAL"] = "Nein, die Mappe lässt deine persönliche Betroffenheit weg und im Anhang das Gespräch von „{0}“"
|
||||
|
||||
-- Discard draft
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2273967454"] = "Entwurf verwerfen"
|
||||
|
||||
@@ -1905,17 +1944,20 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2679230892"]
|
||||
-- Confirm
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2709768687"] = "Bestätigen"
|
||||
|
||||
-- Yes, the folder holds your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2791825400"] = "Ja, die Mappe enthält Ihre persönliche Betroffenheit und im Anhang das Gespräch von „{0}“"
|
||||
|
||||
-- Yes, the folder holds your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2791825400::INFORMAL"] = "Ja, die Mappe enthält deine persönliche Betroffenheit und im Anhang das Gespräch von „{0}“"
|
||||
|
||||
-- A work state changed since you confirmed this material. Please create it anew, or check whether it still fits.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2881263992"] = "Seit Sie dieses Material bestätigt haben, hat sich ein Arbeitsstand geändert. Bitte erstellen Sie es neu, oder prüfen Sie, ob es noch passt."
|
||||
|
||||
-- A work state changed since you confirmed this material. Please create it anew, or check whether it still fits.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2881263992::INFORMAL"] = "Seit du dieses Material bestätigt hast, hat sich ein Arbeitsstand geändert. Bitte erstelle es neu, oder prüfe, ob es noch passt."
|
||||
|
||||
-- Add the conversations as an appendix
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3185544181"] = "Gespräche als Anhang aufnehmen"
|
||||
|
||||
-- Add the conversations as an appendix
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3185544181::INFORMAL"] = "Gespräche als Anhang aufnehmen"
|
||||
-- Add the conversations as an appendix?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2968063742"] = "Gespräche als Anhang aufnehmen?"
|
||||
|
||||
-- Being written
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3204128327"] = "Wird geschrieben"
|
||||
@@ -1923,6 +1965,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3204128327"]
|
||||
-- Edit
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3267849393"] = "Bearbeiten"
|
||||
|
||||
-- Yes, the folder ends with the conversations of all steps
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3386507601"] = "Ja, die Mappe endet mit den Gesprächen aller Schritte"
|
||||
|
||||
-- AI Studio could not save this material. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3460571754"] = "AI Studio konnte dieses Material nicht speichern. Bitte versuchen Sie es erneut."
|
||||
|
||||
@@ -1938,11 +1983,8 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3898821075"]
|
||||
-- Create all materials in parallel
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3926195314"] = "Alle Materialien parallel erstellen"
|
||||
|
||||
-- Include personal disclosures
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T4086721095"] = "Persönliche Angaben aufnehmen"
|
||||
|
||||
-- Include personal disclosures
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T4086721095::INFORMAL"] = "Persönliche Angaben aufnehmen"
|
||||
-- Include personal disclosures?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3989412584"] = "Persönliche Angaben aufnehmen?"
|
||||
|
||||
-- Outdated
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T724820205"] = "Veraltet"
|
||||
@@ -1953,6 +1995,225 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T934952176"]
|
||||
-- Confirmed
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T942512558"] = "Bestätigt"
|
||||
|
||||
-- Replaced
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1120626693"] = "Ersetzt"
|
||||
|
||||
-- Names count only when they stand here. The lab suggests the names of other participants it knows; check them, and add your own name and everything else which points to you or to the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1161078151"] = "Namen zählen nur, wenn sie hier stehen. Das Lab schlägt die Namen anderer Teilnehmender vor, die es kennt. Prüfen Sie sie, und ergänzen Sie Ihren eigenen Namen sowie alles Weitere, was auf Sie oder die Veranstaltung hinweist."
|
||||
|
||||
-- Names count only when they stand here. The lab suggests the names of other participants it knows; check them, and add your own name and everything else which points to you or to the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1161078151::INFORMAL"] = "Namen zählen nur, wenn sie hier stehen. Das Lab schlägt die Namen anderer Teilnehmender vor, die es kennt. Prüfe sie, und ergänze deinen eigenen Namen sowie alles Weitere, was auf dich oder die Veranstaltung hinweist."
|
||||
|
||||
-- Create preview
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1209353201"] = "Vorschau erstellen"
|
||||
|
||||
-- Nothing was replaced.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1254778913"] = "Es wurde nichts ersetzt."
|
||||
|
||||
-- Create export
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1293027637"] = "Export erstellen"
|
||||
|
||||
-- Check the preview and change it where needed. AI Studio anonymizes your changes once more when you leave the field.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1446555152"] = "Überprüfen Sie die Vorschau und passen Sie sie bei Bedarf an. AI Studio anonymisiert Ihre Änderungen erneut, sobald Sie das Feld verlassen."
|
||||
|
||||
-- Check the preview and change it where needed. AI Studio anonymizes your changes once more when you leave the field.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1446555152::INFORMAL"] = "Überprüfe die Vorschau und passe sie bei Bedarf an. AI Studio anonymisiert deine Änderungen erneut, sobald du das Feld verlässt."
|
||||
|
||||
-- Shared session
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1494358904"] = "Geteilte Sitzung"
|
||||
|
||||
-- No, the file leaves out your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1859480999"] = "Nein, die Datei lässt Ihre persönliche Betroffenheit und das Gespräch von „{0}“ weg"
|
||||
|
||||
-- No, the file leaves out your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1859480999::INFORMAL"] = "Nein, die Datei lässt deine persönliche Betroffenheit und das Gespräch von „{0}“ weg"
|
||||
|
||||
-- What AI Studio replaced ({0})
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2050374220"] = "Was AI Studio ersetzt hat ({0})"
|
||||
|
||||
-- Share with the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2202378897"] = "Mit dem Forschungsteam teilen"
|
||||
|
||||
-- Yes, the file holds your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2300527633"] = "Ja, die Datei enthält Ihre persönliche Betroffenheit und das Gespräch von „{0}“"
|
||||
|
||||
-- Yes, the file holds your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2300527633::INFORMAL"] = "Ja, die Datei enthält deine persönliche Betroffenheit und das Gespräch von „{0}“"
|
||||
|
||||
-- Placeholder
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2307342650"] = "Platzhalter"
|
||||
|
||||
-- Send it
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2402701656"] = "Versenden"
|
||||
|
||||
-- Further terms, one per line
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T275020693"] = "Weitere Begriffe, einer pro Zeile"
|
||||
|
||||
-- What AI Studio removes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2839457006"] = "Was AI Studio entfernt"
|
||||
|
||||
-- Create the preview anew? Your changes in the preview will be lost.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2839487925"] = "Vorschau neu erstellen? Ihre Änderungen an der Vorschau gehen verloren."
|
||||
|
||||
-- Create the preview anew? Your changes in the preview will be lost.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2839487925::INFORMAL"] = "Vorschau neu erstellen? Deine Änderungen an der Vorschau gehen verloren."
|
||||
|
||||
-- AI Studio cannot open this preparation. Its files may be damaged, or a newer version of AI Studio stored them. AI Studio leaves the files unchanged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3058916397"] = "AI Studio kann diese Vorbereitung nicht öffnen. Die Dateien sind möglicherweise beschädigt oder wurden mit einer neueren Version von AI Studio gespeichert. AI Studio lässt die Dateien unverändert."
|
||||
|
||||
-- AI Studio cannot open this preparation. Its files may be damaged, or a newer version of AI Studio stored them. AI Studio leaves the files unchanged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3058916397::INFORMAL"] = "AI Studio kann diese Vorbereitung nicht öffnen. Die Dateien sind möglicherweise beschädigt oder wurden mit einer neueren Version von AI Studio gespeichert. AI Studio lässt die Dateien unverändert."
|
||||
|
||||
-- Prepare the mail to the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3134014858"] = "E-Mail an das Forschungsteam vorbereiten"
|
||||
|
||||
-- For research purposes, your data will be evaluated and analyzed in pseudonymized form.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T343267095"] = "Ihre Daten werden zu Forschungszwecken pseudonymisiert ausgewertet und analysiert."
|
||||
|
||||
-- For research purposes, your data will be evaluated and analyzed in pseudonymized form.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T343267095::INFORMAL"] = "Deine Daten werden zu Forschungszwecken pseudonymisiert ausgewertet und analysiert."
|
||||
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3448155331"] = "Schließen"
|
||||
|
||||
-- Names of other people, one per line
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3883857514"] = "Namen weiterer Personen, einer pro Zeile"
|
||||
|
||||
-- Session to share
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T390620119"] = "Zu teilende Sitzung"
|
||||
|
||||
-- {0} ({1}×)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3914405868"] = "{0} ({1}×)"
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- E.g., your institution, the name of the event, or a place.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3960545771"] = "Z. B. Ihre Institution, der Name der Veranstaltung oder ein Ort."
|
||||
|
||||
-- E.g., your institution, the name of the event, or a place.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3960545771::INFORMAL"] = "Z. B. deine Institution, der Name der Veranstaltung oder ein Ort."
|
||||
|
||||
-- Include personal disclosures?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3989412584"] = "Persönliche Angaben aufnehmen?"
|
||||
|
||||
-- Preview
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T4258942199"] = "Vorschau"
|
||||
|
||||
-- The file holds your details, your confirmed work states, your conversations, and the materials you confirmed. It never holds the name of your preparation, your material, the research on the web, or the date, the place, and the web page of the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T47760612"] = "Die Datei enthält Ihre Angaben, Ihre bestätigten Arbeitsstände, Ihre Gespräche und die Materialien, die Sie bestätigt haben. Sie enthält nie den Namen Ihrer Vorbereitung, Ihr Material, die Recherche im Web oder den Termin, den Ort und die Webseite der Veranstaltung."
|
||||
|
||||
-- The file holds your details, your confirmed work states, your conversations, and the materials you confirmed. It never holds the name of your preparation, your material, the research on the web, or the date, the place, and the web page of the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T47760612::INFORMAL"] = "Die Datei enthält deine Angaben, deine bestätigten Arbeitsstände, deine Gespräche und die Materialien, die du bestätigt hast. Sie enthält nie den Namen deiner Vorbereitung, dein Material, die Recherche im Web oder den Termin, den Ort und die Webseite der Veranstaltung."
|
||||
|
||||
-- Would you like to share your session with the research team of the BeyondFacts Lab, so that it can develop the lab further? Before you share it, AI Studio removes the personal details it recognizes: links, e-mail addresses, paths, phone numbers, and the names and terms below. It follows fixed rules and may miss something, so please check the preview before you send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T483133022"] = "Möchten Sie Ihre Sitzung mit dem Forschungsteam des BeyondFacts Lab teilen, damit es das Lab weiterentwickeln kann? Bevor Sie sie teilen, entfernt AI Studio die persönlichen Angaben, die es erkennt: Links, E-Mail-Adressen, Pfade, Telefonnummern sowie die unten aufgeführten Namen und Begriffe. Die Erkennung folgt festen Regeln und kann etwas übersehen. Bitte prüfen Sie daher die Vorschau, bevor Sie die Sitzung senden."
|
||||
|
||||
-- Would you like to share your session with the research team of the BeyondFacts Lab, so that it can develop the lab further? Before you share it, AI Studio removes the personal details it recognizes: links, e-mail addresses, paths, phone numbers, and the names and terms below. It follows fixed rules and may miss something, so please check the preview before you send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T483133022::INFORMAL"] = "Möchtest du deine Sitzung mit dem Forschungsteam des BeyondFacts Lab teilen, damit es das Lab weiterentwickeln kann? Bevor du sie teilst, entfernt AI Studio die persönlichen Angaben, die es erkennt: Links, E-Mail-Adressen, Pfade, Telefonnummern sowie die unten aufgeführten Namen und Begriffe. Die Erkennung folgt festen Regeln und kann etwas übersehen. Bitte prüfe daher die Vorschau, bevor du die Sitzung sendest."
|
||||
|
||||
-- Thank you for preparing with the lab!
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T527414024"] = "Vielen Dank, dass Sie sich mit dem Lab vorbereitet haben!"
|
||||
|
||||
-- Thank you for preparing with the lab!
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T527414024::INFORMAL"] = "Vielen Dank, dass du dich mit dem Lab vorbereitet hast!"
|
||||
|
||||
-- First, export the session as a file. Then prepare the mail to the research team, attach the file in your mail program, and send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T667916114"] = "Exportieren Sie zuerst die Sitzung als Datei. Bereiten Sie dann die E-Mail an das Forschungsteam vor, hängen Sie die Datei in Ihrem E-Mail-Programm an und senden Sie die E-Mail ab."
|
||||
|
||||
-- First, export the session as a file. Then prepare the mail to the research team, attach the file in your mail program, and send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T667916114::INFORMAL"] = "Exportiere zuerst die Sitzung als Datei. Bereite dann die E-Mail an das Forschungsteam vor, hänge die Datei in deinem E-Mail-Programm an und sende die E-Mail ab."
|
||||
|
||||
-- Not now
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T742223822"] = "Nicht jetzt"
|
||||
|
||||
-- Your mail program sends the mail from your own address, so the research team sees who sends it. AI Studio anonymizes only the content of the file. The research institution stores your pseudonymized data so that nobody can link it to you or to your sender address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T892175069"] = "Ihr E-Mail-Programm sendet die E-Mail von Ihrer eigenen Adresse, sodass das Forschungsteam sieht, wer sie sendet. AI Studio anonymisiert nur den Inhalt der Datei. Die Forschungseinrichtung speichert Ihre pseudonymisierten Daten so, dass kein Zusammenhang mit Ihnen und Ihrer Absenderadresse möglich ist."
|
||||
|
||||
-- Your mail program sends the mail from your own address, so the research team sees who sends it. AI Studio anonymizes only the content of the file. The research institution stores your pseudonymized data so that nobody can link it to you or to your sender address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T892175069::INFORMAL"] = "Dein E-Mail-Programm sendet die E-Mail von deiner eigenen Adresse, sodass das Forschungsteam sieht, wer sie sendet. AI Studio anonymisiert nur den Inhalt der Datei. Die Forschungseinrichtung speichert deine pseudonymisierten Daten so, dass kein Zusammenhang mit dir und deiner Absenderadresse möglich ist."
|
||||
|
||||
-- Create preview anew
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T953794800"] = "Vorschau neu erstellen"
|
||||
|
||||
-- Your name
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T973497277"] = "Ihr Name"
|
||||
|
||||
-- Your name
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T973497277::INFORMAL"] = "Dein Name"
|
||||
|
||||
-- A shared session of the BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMAIL::T3284671944"] = "Eine geteilte Sitzung des BeyondFacts Lab"
|
||||
|
||||
-- Hello, attached is a session of the BeyondFacts Lab, which I share for the research on the lab. Before I exported it, AI Studio removed the personal details it recognized. Kind regards
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMAIL::T3622173728"] = "Guten Tag,\n\nanbei sende ich eine Sitzung des BeyondFacts Lab, die ich für die Forschung zum Lab teile. Vor dem Export hat AI Studio die persönlichen Angaben entfernt, die es erkannt hat.\n\nMit freundlichen Grüßen"
|
||||
|
||||
-- No confirmed work state.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1085223273"] = "Kein bestätigter Arbeitsstand."
|
||||
|
||||
-- No conversation.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1296316689"] = "Kein Gespräch."
|
||||
|
||||
-- Research on the web
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1322369504"] = "Recherche im Web"
|
||||
|
||||
-- Shared on
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1703957189"] = "Geteilt am"
|
||||
|
||||
-- Personal disclosures
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1899851975"] = "Persönliche Angaben"
|
||||
|
||||
-- left out
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1940843988"] = "ausgelassen"
|
||||
|
||||
-- Details
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T247611973"] = "Angaben"
|
||||
|
||||
-- Conversation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2640583206"] = "Gespräch"
|
||||
|
||||
-- Not confirmed.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2652477427"] = "Nicht bestätigt."
|
||||
|
||||
-- {0} documents, {1} recordings, {2} images
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2667668036"] = "{0} Dokumente, {1} Aufnahmen, {2} Bilder"
|
||||
|
||||
-- none
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2913447899"] = "keine"
|
||||
|
||||
-- No details.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2977469358"] = "Keine Angaben."
|
||||
|
||||
-- The confirmed work state holds nothing to share.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3153189308"] = "Der bestätigte Arbeitsstand enthält nichts, was geteilt werden kann."
|
||||
|
||||
-- included
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3202028217"] = "enthalten"
|
||||
|
||||
-- Person
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3278826400"] = "Person"
|
||||
|
||||
-- Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3303746196"] = "Lab"
|
||||
|
||||
-- Material
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3419754368"] = "Material"
|
||||
|
||||
-- Confirmed work state
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3444987166"] = "Bestätigter Arbeitsstand"
|
||||
|
||||
-- About this session
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3460157970"] = "Über diese Sitzung"
|
||||
|
||||
-- BeyondFacts Lab: a shared session
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3731741448"] = "BeyondFacts Lab: eine geteilte Sitzung"
|
||||
|
||||
-- Left out, as it may contain personal disclosures.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3986361996"] = "Ausgelassen, da es persönliche Angaben enthalten kann."
|
||||
|
||||
-- Version of AI Studio
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T728223168"] = "Version von AI Studio"
|
||||
|
||||
-- Test situation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Testsituation"
|
||||
|
||||
|
||||
+271
-10
@@ -771,6 +771,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::
|
||||
-- Cannot be opened
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T1981873292"] = "Cannot be opened"
|
||||
|
||||
-- Share with the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2202378897"] = "Share with the research team"
|
||||
|
||||
-- Copy preparation ID
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T2344012850"] = "Copy preparation ID"
|
||||
|
||||
@@ -906,6 +909,24 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::
|
||||
-- Permanently delete the preparation '{0}' with all of its steps, chats, material, and work states?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BEYONDFACTSLABASSISTANT::T98434423::INFORMAL"] = "Permanently delete the preparation '{0}' with all of its steps, chats, material, and work states?"
|
||||
|
||||
-- Link
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T2382947721"] = "Link"
|
||||
|
||||
-- E-mail address
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T2411920080"] = "E-mail address"
|
||||
|
||||
-- Person
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T3278826400"] = "Person"
|
||||
|
||||
-- Path
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T3949388886"] = "Path"
|
||||
|
||||
-- Term
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T586836727"] = "Term"
|
||||
|
||||
-- Phone number
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLANONYMIZER::T987655160"] = "Phone number"
|
||||
|
||||
-- Stop
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLCHAT::T1266644741"] = "Stop"
|
||||
|
||||
@@ -1317,6 +1338,15 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2001857
|
||||
-- Work through the seven steps, in any order you like. Each step has the form 'Your details' and a conversation of its own.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2001857602::INFORMAL"] = "Work through the seven steps, in any order you like. Each step has the form 'Your details' and a conversation of its own."
|
||||
|
||||
-- With 'Share with the research team' above your preparation, you can share your session with the research team of the lab, so that it can develop the lab further; the lab also asks once you confirm the work state of the last step for the first time. Sharing is voluntary. Before you share, AI Studio removes the personal details it recognizes, and you check the preview, since its rules may miss something. You export the file and send it with your own mail program, which shows your address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2242659102"] = "With 'Share with the research team' above your preparation, you can share your session with the research team of the lab, so that it can develop the lab further; the lab also asks once you confirm the work state of the last step for the first time. Sharing is voluntary. Before you share, AI Studio removes the personal details it recognizes, and you check the preview, since its rules may miss something. You export the file and send it with your own mail program, which shows your address."
|
||||
|
||||
-- With 'Share with the research team' above your preparation, you can share your session with the research team of the lab, so that it can develop the lab further; the lab also asks once you confirm the work state of the last step for the first time. Sharing is voluntary. Before you share, AI Studio removes the personal details it recognizes, and you check the preview, since its rules may miss something. You export the file and send it with your own mail program, which shows your address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2242659102::INFORMAL"] = "With 'Share with the research team' above your preparation, you can share your session with the research team of the lab, so that it can develop the lab further; the lab also asks once you confirm the work state of the last step for the first time. Sharing is voluntary. Before you share, AI Studio removes the personal details it recognizes, and you check the preview, since its rules may miss something. You export the file and send it with your own mail program, which shows your address."
|
||||
|
||||
-- Sharing with the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2278322216"] = "Sharing with the research team"
|
||||
|
||||
-- How to recognize good results
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLINTRODUCTION::T2310692629"] = "How to recognize good results"
|
||||
|
||||
@@ -1860,6 +1890,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T1597401023"]
|
||||
-- A work state changed since the lab wrote this draft. You can create it anew.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T1597401023::INFORMAL"] = "A work state changed since the lab wrote this draft. You can create it anew."
|
||||
|
||||
-- No, the folder holds no conversations
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T1599137736"] = "No, the folder holds no conversations"
|
||||
|
||||
-- The lab writes these materials from your confirmed work states. Check and edit each of them, and confirm it. Then you can export it as a document.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T182456751"] = "The lab writes these materials from your confirmed work states. Check and edit each of them, and confirm it. Then you can export it as a document."
|
||||
|
||||
@@ -1881,6 +1914,12 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2174778813"]
|
||||
-- These steps have no confirmed work state yet: {0}. The materials use only what you confirmed and name what is still open.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2174778813::INFORMAL"] = "These steps have no confirmed work state yet: {0}. The materials use only what you confirmed and name what is still open."
|
||||
|
||||
-- No, the folder leaves out your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T219251710"] = "No, the folder leaves out your personal involvement and, in the appendix, the conversation of '{0}'"
|
||||
|
||||
-- No, the folder leaves out your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T219251710::INFORMAL"] = "No, the folder leaves out your personal involvement and, in the appendix, the conversation of '{0}'"
|
||||
|
||||
-- Discard draft
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2273967454"] = "Discard draft"
|
||||
|
||||
@@ -1905,17 +1944,20 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2679230892"]
|
||||
-- Confirm
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2709768687"] = "Confirm"
|
||||
|
||||
-- Yes, the folder holds your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2791825400"] = "Yes, the folder holds your personal involvement and, in the appendix, the conversation of '{0}'"
|
||||
|
||||
-- Yes, the folder holds your personal involvement and, in the appendix, the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2791825400::INFORMAL"] = "Yes, the folder holds your personal involvement and, in the appendix, the conversation of '{0}'"
|
||||
|
||||
-- A work state changed since you confirmed this material. Please create it anew, or check whether it still fits.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2881263992"] = "A work state changed since you confirmed this material. Please create it anew, or check whether it still fits."
|
||||
|
||||
-- A work state changed since you confirmed this material. Please create it anew, or check whether it still fits.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2881263992::INFORMAL"] = "A work state changed since you confirmed this material. Please create it anew, or check whether it still fits."
|
||||
|
||||
-- Add the conversations as an appendix
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3185544181"] = "Add the conversations as an appendix"
|
||||
|
||||
-- Add the conversations as an appendix
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3185544181::INFORMAL"] = "Add the conversations as an appendix"
|
||||
-- Add the conversations as an appendix?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T2968063742"] = "Add the conversations as an appendix?"
|
||||
|
||||
-- Being written
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3204128327"] = "Being written"
|
||||
@@ -1923,6 +1965,9 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3204128327"]
|
||||
-- Edit
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3267849393"] = "Edit"
|
||||
|
||||
-- Yes, the folder ends with the conversations of all steps
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3386507601"] = "Yes, the folder ends with the conversations of all steps"
|
||||
|
||||
-- AI Studio could not save this material. Please try again.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3460571754"] = "AI Studio could not save this material. Please try again."
|
||||
|
||||
@@ -1938,11 +1983,8 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3898821075"]
|
||||
-- Create all materials in parallel
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3926195314"] = "Create all materials in parallel"
|
||||
|
||||
-- Include personal disclosures
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T4086721095"] = "Include personal disclosures"
|
||||
|
||||
-- Include personal disclosures
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T4086721095::INFORMAL"] = "Include personal disclosures"
|
||||
-- Include personal disclosures?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T3989412584"] = "Include personal disclosures?"
|
||||
|
||||
-- Outdated
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T724820205"] = "Outdated"
|
||||
@@ -1953,6 +1995,225 @@ UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T934952176"]
|
||||
-- Confirmed
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLRESULTS::T942512558"] = "Confirmed"
|
||||
|
||||
-- Replaced
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1120626693"] = "Replaced"
|
||||
|
||||
-- Names count only when they stand here. The lab suggests the names of other participants it knows; check them, and add your own name and everything else which points to you or to the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1161078151"] = "Names count only when they stand here. The lab suggests the names of other participants it knows; check them, and add your own name and everything else which points to you or to the event."
|
||||
|
||||
-- Names count only when they stand here. The lab suggests the names of other participants it knows; check them, and add your own name and everything else which points to you or to the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1161078151::INFORMAL"] = "Names count only when they stand here. The lab suggests the names of other participants it knows; check them, and add your own name and everything else which points to you or to the event."
|
||||
|
||||
-- Create preview
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1209353201"] = "Create preview"
|
||||
|
||||
-- Nothing was replaced.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1254778913"] = "Nothing was replaced."
|
||||
|
||||
-- Create export
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1293027637"] = "Create export"
|
||||
|
||||
-- Check the preview and change it where needed. AI Studio anonymizes your changes once more when you leave the field.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1446555152"] = "Check the preview and change it where needed. AI Studio anonymizes your changes once more when you leave the field."
|
||||
|
||||
-- Check the preview and change it where needed. AI Studio anonymizes your changes once more when you leave the field.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1446555152::INFORMAL"] = "Check the preview and change it where needed. AI Studio anonymizes your changes once more when you leave the field."
|
||||
|
||||
-- Shared session
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1494358904"] = "Shared session"
|
||||
|
||||
-- No, the file leaves out your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1859480999"] = "No, the file leaves out your personal involvement and the conversation of '{0}'"
|
||||
|
||||
-- No, the file leaves out your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T1859480999::INFORMAL"] = "No, the file leaves out your personal involvement and the conversation of '{0}'"
|
||||
|
||||
-- What AI Studio replaced ({0})
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2050374220"] = "What AI Studio replaced ({0})"
|
||||
|
||||
-- Share with the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2202378897"] = "Share with the research team"
|
||||
|
||||
-- Yes, the file holds your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2300527633"] = "Yes, the file holds your personal involvement and the conversation of '{0}'"
|
||||
|
||||
-- Yes, the file holds your personal involvement and the conversation of '{0}'
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2300527633::INFORMAL"] = "Yes, the file holds your personal involvement and the conversation of '{0}'"
|
||||
|
||||
-- Placeholder
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2307342650"] = "Placeholder"
|
||||
|
||||
-- Send it
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2402701656"] = "Send it"
|
||||
|
||||
-- Further terms, one per line
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T275020693"] = "Further terms, one per line"
|
||||
|
||||
-- What AI Studio removes
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2839457006"] = "What AI Studio removes"
|
||||
|
||||
-- Create the preview anew? Your changes in the preview will be lost.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2839487925"] = "Create the preview anew? Your changes in the preview will be lost."
|
||||
|
||||
-- Create the preview anew? Your changes in the preview will be lost.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T2839487925::INFORMAL"] = "Create the preview anew? Your changes in the preview will be lost."
|
||||
|
||||
-- AI Studio cannot open this preparation. Its files may be damaged, or a newer version of AI Studio stored them. AI Studio leaves the files unchanged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3058916397"] = "AI Studio cannot open this preparation. Its files may be damaged, or a newer version of AI Studio stored them. AI Studio leaves the files unchanged."
|
||||
|
||||
-- AI Studio cannot open this preparation. Its files may be damaged, or a newer version of AI Studio stored them. AI Studio leaves the files unchanged.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3058916397::INFORMAL"] = "AI Studio cannot open this preparation. Its files may be damaged, or a newer version of AI Studio stored them. AI Studio leaves the files unchanged."
|
||||
|
||||
-- Prepare the mail to the research team
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3134014858"] = "Prepare the mail to the research team"
|
||||
|
||||
-- For research purposes, your data will be evaluated and analyzed in pseudonymized form.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T343267095"] = "For research purposes, your data will be evaluated and analyzed in pseudonymized form."
|
||||
|
||||
-- For research purposes, your data will be evaluated and analyzed in pseudonymized form.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T343267095::INFORMAL"] = "For research purposes, your data will be evaluated and analyzed in pseudonymized form."
|
||||
|
||||
-- Close
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3448155331"] = "Close"
|
||||
|
||||
-- Names of other people, one per line
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3883857514"] = "Names of other people, one per line"
|
||||
|
||||
-- Session to share
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T390620119"] = "Session to share"
|
||||
|
||||
-- {0} ({1}×)
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3914405868"] = "{0} ({1}×)"
|
||||
|
||||
-- BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3956097526"] = "BeyondFacts Lab"
|
||||
|
||||
-- E.g., your institution, the name of the event, or a place.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3960545771"] = "E.g., your institution, the name of the event, or a place."
|
||||
|
||||
-- E.g., your institution, the name of the event, or a place.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3960545771::INFORMAL"] = "E.g., your institution, the name of the event, or a place."
|
||||
|
||||
-- Include personal disclosures?
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T3989412584"] = "Include personal disclosures?"
|
||||
|
||||
-- Preview
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T4258942199"] = "Preview"
|
||||
|
||||
-- The file holds your details, your confirmed work states, your conversations, and the materials you confirmed. It never holds the name of your preparation, your material, the research on the web, or the date, the place, and the web page of the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T47760612"] = "The file holds your details, your confirmed work states, your conversations, and the materials you confirmed. It never holds the name of your preparation, your material, the research on the web, or the date, the place, and the web page of the event."
|
||||
|
||||
-- The file holds your details, your confirmed work states, your conversations, and the materials you confirmed. It never holds the name of your preparation, your material, the research on the web, or the date, the place, and the web page of the event.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T47760612::INFORMAL"] = "The file holds your details, your confirmed work states, your conversations, and the materials you confirmed. It never holds the name of your preparation, your material, the research on the web, or the date, the place, and the web page of the event."
|
||||
|
||||
-- Would you like to share your session with the research team of the BeyondFacts Lab, so that it can develop the lab further? Before you share it, AI Studio removes the personal details it recognizes: links, e-mail addresses, paths, phone numbers, and the names and terms below. It follows fixed rules and may miss something, so please check the preview before you send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T483133022"] = "Would you like to share your session with the research team of the BeyondFacts Lab, so that it can develop the lab further? Before you share it, AI Studio removes the personal details it recognizes: links, e-mail addresses, paths, phone numbers, and the names and terms below. It follows fixed rules and may miss something, so please check the preview before you send it."
|
||||
|
||||
-- Would you like to share your session with the research team of the BeyondFacts Lab, so that it can develop the lab further? Before you share it, AI Studio removes the personal details it recognizes: links, e-mail addresses, paths, phone numbers, and the names and terms below. It follows fixed rules and may miss something, so please check the preview before you send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T483133022::INFORMAL"] = "Would you like to share your session with the research team of the BeyondFacts Lab, so that it can develop the lab further? Before you share it, AI Studio removes the personal details it recognizes: links, e-mail addresses, paths, phone numbers, and the names and terms below. It follows fixed rules and may miss something, so please check the preview before you send it."
|
||||
|
||||
-- Thank you for preparing with the lab!
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T527414024"] = "Thank you for preparing with the lab!"
|
||||
|
||||
-- Thank you for preparing with the lab!
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T527414024::INFORMAL"] = "Thank you for preparing with the lab!"
|
||||
|
||||
-- First, export the session as a file. Then prepare the mail to the research team, attach the file in your mail program, and send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T667916114"] = "First, export the session as a file. Then prepare the mail to the research team, attach the file in your mail program, and send it."
|
||||
|
||||
-- First, export the session as a file. Then prepare the mail to the research team, attach the file in your mail program, and send it.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T667916114::INFORMAL"] = "First, export the session as a file. Then prepare the mail to the research team, attach the file in your mail program, and send it."
|
||||
|
||||
-- Not now
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T742223822"] = "Not now"
|
||||
|
||||
-- Your mail program sends the mail from your own address, so the research team sees who sends it. AI Studio anonymizes only the content of the file. The research institution stores your pseudonymized data so that nobody can link it to you or to your sender address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T892175069"] = "Your mail program sends the mail from your own address, so the research team sees who sends it. AI Studio anonymizes only the content of the file. The research institution stores your pseudonymized data so that nobody can link it to you or to your sender address."
|
||||
|
||||
-- Your mail program sends the mail from your own address, so the research team sees who sends it. AI Studio anonymizes only the content of the file. The research institution stores your pseudonymized data so that nobody can link it to you or to your sender address.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T892175069::INFORMAL"] = "Your mail program sends the mail from your own address, so the research team sees who sends it. AI Studio anonymizes only the content of the file. The research institution stores your pseudonymized data so that nobody can link it to you or to your sender address."
|
||||
|
||||
-- Create preview anew
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T953794800"] = "Create preview anew"
|
||||
|
||||
-- Your name
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T973497277"] = "Your name"
|
||||
|
||||
-- Your name
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREDIALOG::T973497277::INFORMAL"] = "Your name"
|
||||
|
||||
-- A shared session of the BeyondFacts Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMAIL::T3284671944"] = "A shared session of the BeyondFacts Lab"
|
||||
|
||||
-- Hello, attached is a session of the BeyondFacts Lab, which I share for the research on the lab. Before I exported it, AI Studio removed the personal details it recognized. Kind regards
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMAIL::T3622173728"] = "Hello,\n\nattached is a session of the BeyondFacts Lab, which I share for the research on the lab. Before I exported it, AI Studio removed the personal details it recognized.\n\nKind regards"
|
||||
|
||||
-- No confirmed work state.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1085223273"] = "No confirmed work state."
|
||||
|
||||
-- No conversation.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1296316689"] = "No conversation."
|
||||
|
||||
-- Research on the web
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1322369504"] = "Research on the web"
|
||||
|
||||
-- Shared on
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1703957189"] = "Shared on"
|
||||
|
||||
-- Personal disclosures
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1899851975"] = "Personal disclosures"
|
||||
|
||||
-- left out
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T1940843988"] = "left out"
|
||||
|
||||
-- Details
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T247611973"] = "Details"
|
||||
|
||||
-- Conversation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2640583206"] = "Conversation"
|
||||
|
||||
-- Not confirmed.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2652477427"] = "Not confirmed."
|
||||
|
||||
-- {0} documents, {1} recordings, {2} images
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2667668036"] = "{0} documents, {1} recordings, {2} images"
|
||||
|
||||
-- none
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2913447899"] = "none"
|
||||
|
||||
-- No details.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T2977469358"] = "No details."
|
||||
|
||||
-- The confirmed work state holds nothing to share.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3153189308"] = "The confirmed work state holds nothing to share."
|
||||
|
||||
-- included
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3202028217"] = "included"
|
||||
|
||||
-- Person
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3278826400"] = "Person"
|
||||
|
||||
-- Lab
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3303746196"] = "Lab"
|
||||
|
||||
-- Material
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3419754368"] = "Material"
|
||||
|
||||
-- Confirmed work state
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3444987166"] = "Confirmed work state"
|
||||
|
||||
-- About this session
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3460157970"] = "About this session"
|
||||
|
||||
-- BeyondFacts Lab: a shared session
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3731741448"] = "BeyondFacts Lab: a shared session"
|
||||
|
||||
-- Left out, as it may contain personal disclosures.
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T3986361996"] = "Left out, as it may contain personal disclosures."
|
||||
|
||||
-- Version of AI Studio
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSHAREMODELBUILDER::T728223168"] = "Version of AI Studio"
|
||||
|
||||
-- Test situation
|
||||
UI_TEXT_CONTENT["AISTUDIO::ASSISTANTS::BEYONDFACTSLAB::BFLSTEPEXTENSIONS::T1049923473"] = "Test situation"
|
||||
|
||||
|
||||
@@ -50,6 +50,9 @@ public static class AppIcons
|
||||
/// <summary>Stands for a policy of the document analysis, which defines how documents are analyzed.</summary>
|
||||
public const string ANALYSIS_POLICY = LucideIcons.CLIPBOARD_LIST;
|
||||
|
||||
/// <summary>Marks what anonymizing replaced, such as the names in a session shared from the BeyondFacts Lab.</summary>
|
||||
public const string ANONYMIZED = LucideIcons.VENETIAN_MASK;
|
||||
|
||||
/// <summary>Marks the streamed answer of an AI, such as when generating or reading it fails.</summary>
|
||||
public const string ANSWER_STREAM = LucideIcons.ACTIVITY;
|
||||
|
||||
@@ -1013,6 +1016,9 @@ public static class AppIcons
|
||||
/// <summary>Shares an item with the native share sheet of the operating system, such as a plugin archive.</summary>
|
||||
public const string SHARE = LucideIcons.SHARE;
|
||||
|
||||
/// <summary>Shares a session with a research team, such as a preparation of the BeyondFacts Lab with the team which develops it.</summary>
|
||||
public const string SHARE_WITH_RESEARCH = LucideIcons.HEART_HANDSHAKE;
|
||||
|
||||
/// <summary>Shows the sidebar, such as the workspaces.</summary>
|
||||
public const string SHOW_SIDEBAR = LucideIcons.PANEL_LEFT_OPEN;
|
||||
|
||||
|
||||
@@ -0,0 +1,184 @@
|
||||
using AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
namespace AIStudio.Tests.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Checks which personal details the BeyondFacts Lab removes from a session before the person shares it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The anonymizer follows rules. These tests describe what the rules promise: the forms in which names appear,
|
||||
/// addresses, paths, and phone numbers, and that a second run changes nothing.
|
||||
/// </remarks>
|
||||
[TestFixture]
|
||||
public sealed class BflAnonymizerTests
|
||||
{
|
||||
[TestCase("Jörg Müller hat geantwortet.")]
|
||||
[TestCase("Joerg Mueller hat geantwortet.")]
|
||||
[TestCase("Jorg Muller hat geantwortet.")]
|
||||
[TestCase("JÖRG MÜLLER hat geantwortet.")]
|
||||
public void AFullNameIsReplacedWithAndWithoutItsUmlauts(string text) => Assert.That(Anonymize(text, ["Jörg Müller"]), Is.EqualTo("[Person 1] hat geantwortet."));
|
||||
|
||||
[Test]
|
||||
public void ANameWrittenOutIsAlsoReplacedWithItsUmlauts() => Assert.That(Anonymize("Herr Müller und Jörg", ["Joerg Mueller"]), Is.EqualTo("[Person 1] und [Person 1]"));
|
||||
|
||||
[TestCase("Prof. Dr. Anna Schmidt spricht.")]
|
||||
[TestCase("Dr. Schmidt spricht.")]
|
||||
[TestCase("Frau Schmidt spricht.")]
|
||||
[TestCase("Prof.Dr. Anna Schmidt spricht.")]
|
||||
[TestCase("Anna spricht.")]
|
||||
public void TheTitlesGoWithTheName(string text) => Assert.That(Anonymize(text, ["Anna Schmidt"]), Is.EqualTo("[Person 1] spricht."));
|
||||
|
||||
[TestCase("Schmidts Studie", "[Person 1]s Studie")]
|
||||
[TestCase("Anna Schmidts Studie", "[Person 1]s Studie")]
|
||||
[TestCase("Anna's talk", "[Person 1]'s talk")]
|
||||
[TestCase("Schmidt’s talk", "[Person 1]’s talk")]
|
||||
[TestCase("„Schmidt“ sagte", "„[Person 1]“ sagte")]
|
||||
public void TheGenitiveStaysAfterThePlaceholder(string text, string expected) => Assert.That(Anonymize(text, ["Anna Schmidt"]), Is.EqualTo(expected));
|
||||
|
||||
[Test]
|
||||
public void TheGenitiveOfANameEndingWithSIsReplaced() => Assert.That(Anonymize("Schmitz' Buch", ["Lena Schmitz"]), Is.EqualTo("[Person 1]' Buch"));
|
||||
|
||||
[Test]
|
||||
public void ALongerNameWinsOverTheShorterOneItContains()
|
||||
{
|
||||
var text = Anonymize("Anna Schmidt-Weber sprach mit Anna Schmidt. Schmidt-Weber widersprach.", ["Anna Schmidt", "Anna Schmidt-Weber"]);
|
||||
Assert.That(text, Is.EqualTo("[Person 2] sprach mit [Person 1]. [Person 2] widersprach."));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void APartWhichSeveralPeopleShareGetsNoNumber()
|
||||
{
|
||||
var text = Anonymize("Schmidt sagte zu Anna Schmidt und Bernd Schmidt: Bernd!", ["Anna Schmidt", "Bernd Schmidt"]);
|
||||
Assert.That(text, Is.EqualTo("[Person] sagte zu [Person 1] und [Person 2]: [Person 2]!"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void APartOfANameOnItsOwnStaysInADoubleNameOfSomebodyElse() => Assert.That(Anonymize("Frau Müller-Schmidt kam.", ["Anna Schmidt"]), Is.EqualTo("Frau Müller-Schmidt kam."));
|
||||
|
||||
[Test]
|
||||
public void APartOfANameOnItsOwnCountsOnlyWithACapitalLetter() => Assert.That(Anonymize("Der Topf ist klein, Klein kam.", ["Eva Klein"]), Is.EqualTo("Der Topf ist klein, [Person 1] kam."));
|
||||
|
||||
[TestCase("anna-schmidt")]
|
||||
[TestCase("anna.schmidt")]
|
||||
[TestCase("annaschmidt")]
|
||||
[TestCase("schmidt_anna")]
|
||||
[TestCase("AnnaSchmidt")]
|
||||
public void TheFormsOfANameInAddressesAreReplaced(string form) => Assert.That(Anonymize($"Profil: @{form} und {form}-lab.de", ["Anna Schmidt"]), Is.EqualTo("Profil: @[Person 1] und [Person 1]-lab.de"));
|
||||
|
||||
[Test]
|
||||
public void ANameWithTheLastNameFirstIsReplaced() => Assert.That(Anonymize("Schmidt, Anna (TUM)", ["Anna Schmidt"]), Is.EqualTo("[Person 1] (TUM)"));
|
||||
|
||||
[Test]
|
||||
public void ANameListedWithTheLastNameFirstIsUnderstood() => Assert.That(Anonymize("Anna Schmidt kam.", ["Schmidt, Anna"]), Is.EqualTo("[Person 1] kam."));
|
||||
|
||||
[Test]
|
||||
public void AParticleBelongsToTheLastName() => Assert.That(Anonymize("Frau von der Leyen und Leyen", ["Ursula von der Leyen"]), Is.EqualTo("[Person 1] und [Person 1]"));
|
||||
|
||||
[TestCase("Siehe https://example.org/team/anna?x=1.", "Siehe [Link].")]
|
||||
[TestCase("(https://example.org/a)", "([Link])")]
|
||||
[TestCase("Auf www.example.org steht es.", "Auf [Link] steht es.")]
|
||||
[TestCase("Das [Programm](https://example.org/programm \"Titel\") lesen", "Das Programm [Link] lesen")]
|
||||
[TestCase("<https://example.org/x>", "[Link]")]
|
||||
[TestCase("Schreib an [Anna](mailto:anna@example.org)", "Schreib an Anna [Link]")]
|
||||
public void LinksAreReplaced(string text, string expected) => Assert.That(Anonymize(text), Is.EqualTo(expected));
|
||||
|
||||
[TestCase("Mail: anna.schmidt+lab@tum.de.", "Mail: [E-mail address].")]
|
||||
[TestCase("jörg@müller.example", "[E-mail address]")]
|
||||
public void EMailAddressesAreReplaced(string text, string expected) => Assert.That(Anonymize(text), Is.EqualTo(expected));
|
||||
|
||||
[TestCase("Datei /Users/anna/Documents/talk.pdf liegt da.", "Datei [Path] liegt da.")]
|
||||
[TestCase(@"Datei C:\Users\anna\talk.docx.", "Datei [Path].")]
|
||||
[TestCase("Datei ~/Notes/talk.md", "Datei [Path]")]
|
||||
[TestCase(@"Freigabe \\server\share\talk.pptx", "Freigabe [Path]")]
|
||||
[TestCase("und/oder, 120 km/h, 1/2", "und/oder, 120 km/h, 1/2")]
|
||||
public void PathsAreReplaced(string text, string expected) => Assert.That(Anonymize(text), Is.EqualTo(expected));
|
||||
|
||||
[TestCase("Tel. +49 89 123456-78.", "Tel. [Phone number].")]
|
||||
[TestCase("Mobil 0176 12345678", "Mobil [Phone number]")]
|
||||
[TestCase("Büro (089) 1234567", "Büro [Phone number]")]
|
||||
[TestCase("Am 05.11.2026 von 08.00-09.30 Uhr", "Am 05.11.2026 von 08.00-09.30 Uhr")]
|
||||
[TestCase("p = 0,05 bei 1234567 Fällen", "p = 0,05 bei 1234567 Fällen")]
|
||||
public void PhoneNumbersAreReplaced(string text, string expected) => Assert.That(Anonymize(text), Is.EqualTo(expected));
|
||||
|
||||
[Test]
|
||||
public void TermsAreReplacedInAnyCase()
|
||||
{
|
||||
var text = BflAnonymizer.Anonymize("Die TUM und die Technische Universität München, kurz tum.", [], ["TUM", "Technische Universität München"]).Text;
|
||||
Assert.That(text, Is.EqualTo("Die [Term 1] und die [Term 2], kurz [Term 1]."));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PlaceholdersStayAlsoWhenATermLooksLikeThem()
|
||||
{
|
||||
var text = BflAnonymizer.Anonymize("[Person 1] nannte [Link] und den Link.", [], ["Link", "Person"]).Text;
|
||||
Assert.That(text, Is.EqualTo("[Person 1] nannte [Link] und den [Term 1]."));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ASecondRunChangesNothing()
|
||||
{
|
||||
const string TEXT = """
|
||||
Prof. Dr. Jörg Müller (joerg.mueller@tum.de, +49 89 123456-78) schrieb in /Users/joerg/Talk/notes.md:
|
||||
Siehe https://example.org/joerg-mueller und [das Programm](https://example.org/p). Müllers These, Eva Klein und die TUM.
|
||||
""";
|
||||
|
||||
var first = BflAnonymizer.Anonymize(TEXT, ["Jörg Müller", "Eva Klein"], ["TUM"]);
|
||||
var second = BflAnonymizer.Anonymize(first.Text, ["Jörg Müller", "Eva Klein"], ["TUM"]);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(second.Text, Is.EqualTo(first.Text));
|
||||
Assert.That(second.Replacements, Is.Empty);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void NoNameRemains()
|
||||
{
|
||||
const string TEXT = """
|
||||
Prof. Dr. Jörg Müller-Lüdenscheidt, Joerg Mueller-Luedenscheidt und Jörg sprachen.
|
||||
Müller-Lüdenscheidts Studie, Dr. Müller-Lüdenscheidt, joerg.mueller-luedenscheidt und @JoergMuellerLuedenscheidt.
|
||||
Ursula von der Leyen, Frau von der Leyen, Leyens Rede, ursula-von-der-leyen.
|
||||
""";
|
||||
|
||||
var text = Anonymize(TEXT, ["Jörg Müller-Lüdenscheidt", "Ursula von der Leyen"]);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
foreach (var part in new[] { "Jörg", "Joerg", "Müller", "Mueller", "Lüdenscheidt", "Luedenscheidt", "Ursula", "Leyen" })
|
||||
Assert.That(text, Does.Not.Contain(part).IgnoreCase, part);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheReportCountsWhatWasReplaced()
|
||||
{
|
||||
var anonymization = BflAnonymizer.Anonymize("Anna Schmidt, Anna Schmidt und Dr. Schmidt: anna@example.org", ["Anna Schmidt"], []);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(anonymization.Replacements, Does.Contain(new BflReplacement("[Person 1]", "Anna Schmidt", 2)));
|
||||
Assert.That(anonymization.Replacements, Does.Contain(new BflReplacement("[Person 1]", "Dr. Schmidt", 1)));
|
||||
Assert.That(anonymization.Replacements, Does.Contain(new BflReplacement("[E-mail address]", "anna@example.org", 1)));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AnEditedTextKeepsTheReportOfTheFirstRun()
|
||||
{
|
||||
var first = BflAnonymizer.Anonymize("Anna Schmidt", ["Anna Schmidt"], []);
|
||||
var later = BflAnonymizer.Anonymize($"{first.Text} und Anna Schmidt", ["Anna Schmidt"], []);
|
||||
var combined = first.CombinedWith(later);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(combined.Text, Is.EqualTo("[Person 1] und [Person 1]"));
|
||||
Assert.That(combined.Replacements, Is.EqualTo(new[] { new BflReplacement("[Person 1]", "Anna Schmidt", 2) }));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EmptyAndRepeatedEntriesGetNoNumber() => Assert.That(Anonymize("Eva Klein und Anna Schmidt", ["", " ", "Eva Klein", "eva klein", "Anna Schmidt"]), Is.EqualTo("[Person 1] und [Person 2]"));
|
||||
|
||||
private static string Anonymize(string text, IReadOnlyList<string>? people = null) => BflAnonymizer.Anonymize(text, people ?? [], []).Text;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
using AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
namespace AIStudio.Tests.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Checks the mailto link with which a person sends a shared session to the research team of the BeyondFacts Lab.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public sealed class BflShareMailTests
|
||||
{
|
||||
[Test]
|
||||
public void TheLinkNamesTheResearchTeamAndItsSubject() => Assert.That(BflShareMail.CreateUri(), Does.StartWith($"mailto:{BflShareMail.RECIPIENT}?subject="));
|
||||
|
||||
[Test]
|
||||
public void TheSubjectAndTheTextAreEncoded()
|
||||
{
|
||||
var uri = BflShareMail.CreateUri("team@example.org", "Müller & Co? 1+1", "Grüße\nund Dank");
|
||||
|
||||
Assert.That(uri, Is.EqualTo("mailto:team@example.org?subject=M%C3%BCller%20%26%20Co%3F%201%2B1&body=Gr%C3%BC%C3%9Fe%0D%0Aund%20Dank"));
|
||||
}
|
||||
|
||||
[TestCase("Eins\nZwei")]
|
||||
[TestCase("Eins\r\nZwei")]
|
||||
[TestCase("Eins\rZwei")]
|
||||
public void EveryLineBreakIsCrLf(string body) => Assert.That(BflShareMail.CreateUri("team@example.org", "Betreff", body), Does.EndWith("&body=Eins%0D%0AZwei"));
|
||||
|
||||
[Test]
|
||||
public void ALongTextLosesItsEndAtAWord()
|
||||
{
|
||||
var body = string.Join(' ', Enumerable.Repeat("Wörter", 600));
|
||||
var uri = BflShareMail.CreateUri("team@example.org", "Betreff", body);
|
||||
var text = Uri.UnescapeDataString(uri[(uri.IndexOf("&body=", StringComparison.Ordinal) + "&body=".Length)..]);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(uri.Length, Is.LessThanOrEqualTo(BflShareMail.MAX_URI_LENGTH));
|
||||
Assert.That(body, Does.StartWith(text));
|
||||
Assert.That(text, Does.EndWith("Wörter"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheTextOfTheLabFitsTheLimit() => Assert.That(BflShareMail.CreateUri(), Does.Contain("&body=").And.Length.LessThanOrEqualTo(BflShareMail.MAX_URI_LENGTH));
|
||||
|
||||
[Test]
|
||||
public void TheTextOfTheLabHasCrLfOnly()
|
||||
{
|
||||
var uri = BflShareMail.CreateUri();
|
||||
Assert.That(uri.Replace("%0D%0A", string.Empty), Does.Not.Contain("%0A").And.Not.Contain("%0D"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
using System.Globalization;
|
||||
|
||||
using AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
namespace AIStudio.Tests.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Checks what a session shared with the research team of the BeyondFacts Lab holds, before anonymizing it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The research team learns how the person worked with the lab. Whatever points to the person or to the event stays
|
||||
/// out: the name of the preparation, the material, the research, the fields which pin down the event, and personal
|
||||
/// disclosures unless the person includes them.
|
||||
/// </remarks>
|
||||
[TestFixture]
|
||||
public sealed class BflShareModelBuilderTests
|
||||
{
|
||||
private static readonly DateTimeOffset NOW = new(2026, 11, 12, 9, 0, 0, TimeSpan.Zero);
|
||||
|
||||
[Test]
|
||||
public void TheFieldsWhichPinDownTheEventStayOut()
|
||||
{
|
||||
var forms = Forms();
|
||||
forms[BflStep.FRAMEWORK].Set(BflFieldCatalog.FORMAT, "Podium-4711");
|
||||
forms[BflStep.FRAMEWORK].Set(BflFieldCatalog.EVENT_DATE, "DATUM-4711");
|
||||
forms[BflStep.FRAMEWORK].Set(BflFieldCatalog.EVENT_LOCATION, "ORT-4711");
|
||||
forms[BflStep.FRAMEWORK].Set(BflFieldCatalog.EVENT_LINK, "https://example.org/LINK-4711");
|
||||
|
||||
var session = Build(forms: forms);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(session, Does.Contain("Podium-4711"));
|
||||
Assert.That(session, Does.Not.Contain("DATUM-4711"));
|
||||
Assert.That(session, Does.Not.Contain("ORT-4711"));
|
||||
Assert.That(session, Does.Not.Contain("LINK-4711"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheLinkDateAndPlaceOfTheEventAreTheFieldsWhichPinItDown()
|
||||
{
|
||||
var identifying = BflFieldCatalog.FIELDS.Where(field => field.IdentifiesEvent).Select(field => field.Id);
|
||||
string[] expected = [BflFieldCatalog.EVENT_DATE, BflFieldCatalog.EVENT_LOCATION, BflFieldCatalog.EVENT_LINK];
|
||||
Assert.That(identifying, Is.EquivalentTo(expected));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheMaterialAppearsOnlyAsItsCount()
|
||||
{
|
||||
var materials = new List<BflMaterial>
|
||||
{
|
||||
new() { MaterialId = Guid.NewGuid(), Name = "Entwurf-Schmidt-4711.pdf", Kind = BflMaterialKind.DOCUMENT, AddedAtUtc = NOW },
|
||||
new() { MaterialId = Guid.NewGuid(), Name = "Probe-4711.m4a", Kind = BflMaterialKind.RECORDING, AddedAtUtc = NOW },
|
||||
new() { MaterialId = Guid.NewGuid(), Name = "Folie-4711.png", Kind = BflMaterialKind.DOCUMENT, AddedAtUtc = NOW },
|
||||
};
|
||||
|
||||
var session = Build(materials: materials);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(session, Does.Not.Contain("4711"));
|
||||
Assert.That(session, Does.Contain("2 documents, 1 recordings, 0 images"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void PersonalDisclosuresStayOutUnlessThePersonIncludesThem()
|
||||
{
|
||||
var forms = Forms();
|
||||
forms[BflStep.VALUES_AND_PERSPECTIVES].Set(BflFieldCatalog.PERSONAL_CONCERN, "FELD-PERSÖNLICH-4711");
|
||||
forms[BflStep.VALUES_AND_PERSPECTIVES].Set(BflFieldCatalog.VALUES_AND_CONCERNS, "Gerechtigkeit");
|
||||
var workStates = new Dictionary<BflStep, BflWorkState>();
|
||||
Confirm(workStates, BflStep.VALUES_AND_PERSPECTIVES, new() { ["VALUES"] = "Offenheit", ["PERSONAL_INVOLVEMENT"] = "STAND-PERSÖNLICH-4711" });
|
||||
var conversations = new Dictionary<BflStep, IReadOnlyList<BflConversationMessage>>
|
||||
{
|
||||
[BflStep.VALUES_AND_PERSPECTIVES] = [new(true, "GESPRÄCH-PERSÖNLICH-4711")],
|
||||
};
|
||||
|
||||
var without = Build(forms: forms, workStates: workStates, conversations: conversations);
|
||||
var with = Build(forms: forms, workStates: workStates, conversations: conversations, includePersonalDisclosures: true);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(without, Does.Not.Contain("PERSÖNLICH-4711"));
|
||||
Assert.That(without, Does.Contain("Gerechtigkeit"));
|
||||
Assert.That(without, Does.Contain("Offenheit"));
|
||||
Assert.That(with, Does.Contain("FELD-PERSÖNLICH-4711"));
|
||||
Assert.That(with, Does.Contain("STAND-PERSÖNLICH-4711"));
|
||||
Assert.That(with, Does.Contain("GESPRÄCH-PERSÖNLICH-4711"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void AConversationOfAStepWithoutPersonalDisclosuresAppearsWithItsSpeakers()
|
||||
{
|
||||
var conversations = new Dictionary<BflStep, IReadOnlyList<BflConversationMessage>>
|
||||
{
|
||||
[BflStep.KNOWLEDGE] = [new(true, "Frage-4711"), new(false, "Antwort-4711")],
|
||||
};
|
||||
|
||||
var session = Build(conversations: conversations);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(session, Does.Contain("**Person:**\n\nFrage-4711\n"));
|
||||
Assert.That(session, Does.Contain("**Lab:**\n\nAntwort-4711\n"));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ChoicesAppearWithTheLabelsOfTheirOptions()
|
||||
{
|
||||
var forms = Forms();
|
||||
forms[BflStep.FRAMEWORK].Set(BflFieldCatalog.EVENT_VISIBILITY, BflFieldCatalog.EVENT_NOT_PUBLIC);
|
||||
var goals = BflFieldCatalog.FIELDS.Single(field => field.Id == BflFieldCatalog.GOAL_DIRECTIONS);
|
||||
forms[BflStep.GOALS_AND_ROLE].SetChoices(goals, [BflFieldCatalog.GOAL_INFORM, BflFieldCatalog.GOAL_MODERATE]);
|
||||
|
||||
var session = Build(forms: forms);
|
||||
var visibility = BflFieldCatalog.FIELDS.Single(field => field.Id == BflFieldCatalog.EVENT_VISIBILITY);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(session, Does.Contain(visibility.Options.Single(option => option.Id == BflFieldCatalog.EVENT_NOT_PUBLIC).Label));
|
||||
Assert.That(session, Does.Contain($"{goals.Options[0].Label}, {goals.Options[1].Label}"));
|
||||
Assert.That(session, Does.Not.Contain(BflFieldCatalog.EVENT_NOT_PUBLIC));
|
||||
Assert.That(session, Does.Not.Contain(BflFieldCatalog.GOAL_INFORM));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DraftsAndTheReflectionFolderNeverAppear()
|
||||
{
|
||||
var workStates = new Dictionary<BflStep, BflWorkState>
|
||||
{
|
||||
[BflStep.KNOWLEDGE] = new() { Draft = new(StringComparer.Ordinal) { ["ESTABLISHED"] = "ENTWURF-4711" } },
|
||||
};
|
||||
|
||||
var results = new Dictionary<BflResultKind, BflResult>
|
||||
{
|
||||
[BflResultKind.POSITION_PAPER] = new() { Draft = new() { Text = "MATERIAL-ENTWURF-4711" } },
|
||||
[BflResultKind.REFLECTION_FOLDER] = new() { Confirmed = new() { Text = "MAPPE-4711" } },
|
||||
};
|
||||
|
||||
Assert.That(Build(workStates: workStates, results: results), Does.Not.Contain("4711"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheConfirmedMaterialsOfTheLabAppearInTheLastStep()
|
||||
{
|
||||
var results = new Dictionary<BflResultKind, BflResult>
|
||||
{
|
||||
[BflResultKind.POSITION_PAPER] = new() { Confirmed = new() { Text = "Position-4711" } },
|
||||
};
|
||||
|
||||
var session = Build(results: results);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(session, Does.Contain($"### {BflResultKind.POSITION_PAPER.Title}\n\nPosition-4711\n"));
|
||||
Assert.That(session.IndexOf("Position-4711", StringComparison.Ordinal), Is.GreaterThan(session.IndexOf($"## {BflStep.RESULTS.Title}\n", StringComparison.Ordinal)));
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheTopicsOfTheResearchAppearByTheirTitles()
|
||||
{
|
||||
var session = Build(researchTopics: [BflResearchTopic.DEBATE, BflResearchTopic.EVENT]);
|
||||
Assert.That(session, Does.Contain($"{BflResearchTopic.EVENT.Title}, {BflResearchTopic.DEBATE.Title}"));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EveryStepAppearsInItsOrder()
|
||||
{
|
||||
var session = Build();
|
||||
var positions = BflStep.All.Select(step => session.IndexOf($"## {step.Title}\n", StringComparison.Ordinal)).ToList();
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(positions, Has.All.GreaterThanOrEqualTo(0));
|
||||
Assert.That(positions, Is.Ordered);
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void EveryLineEndsWithLf()
|
||||
{
|
||||
var forms = Forms();
|
||||
forms[BflStep.KNOWLEDGE].Set(BflFieldCatalog.CORE_THESES, "Erste\r\nZweite");
|
||||
var conversations = new Dictionary<BflStep, IReadOnlyList<BflConversationMessage>>
|
||||
{
|
||||
[BflStep.KNOWLEDGE] = [new(false, "Eins\r\nZwei")],
|
||||
};
|
||||
|
||||
Assert.That(Build(forms: forms, conversations: conversations), Does.Not.Contain("\r"));
|
||||
}
|
||||
|
||||
private static string Build(Dictionary<BflStep, BflStepFormData>? forms = null, IReadOnlyDictionary<BflStep, BflWorkState>? workStates = null, IReadOnlyDictionary<BflResultKind, BflResult>? results = null, IReadOnlyDictionary<BflStep, IReadOnlyList<BflConversationMessage>>? conversations = null, IReadOnlyList<BflMaterial>? materials = null, IReadOnlyCollection<BflResearchTopic>? researchTopics = null, bool includePersonalDisclosures = false) => BflShareModelBuilder.Build(new BflShareInput(
|
||||
forms ?? Forms(),
|
||||
workStates ?? new Dictionary<BflStep, BflWorkState>(),
|
||||
results ?? new Dictionary<BflResultKind, BflResult>(),
|
||||
conversations ?? new Dictionary<BflStep, IReadOnlyList<BflConversationMessage>>(),
|
||||
materials ?? [],
|
||||
researchTopics ?? [],
|
||||
includePersonalDisclosures,
|
||||
"26.11.2",
|
||||
NOW,
|
||||
CultureInfo.InvariantCulture));
|
||||
|
||||
private static Dictionary<BflStep, BflStepFormData> Forms() => BflStep.All.ToDictionary(step => step, _ => new BflStepFormData());
|
||||
|
||||
private static void Confirm(Dictionary<BflStep, BflWorkState> workStates, BflStep step, Dictionary<string, string> sections)
|
||||
{
|
||||
if (!workStates.TryGetValue(step, out var workState))
|
||||
{
|
||||
workState = new BflWorkState();
|
||||
workStates[step] = workState;
|
||||
}
|
||||
|
||||
workState.Confirm(sections, BflStaleness.UpstreamRevisionsOf(step, workStates), BflStaleness.FingerprintOf(new BflStepFormData()), NOW);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
using AIStudio.Assistants.BeyondFactsLab;
|
||||
|
||||
namespace AIStudio.Tests.BeyondFactsLab;
|
||||
|
||||
/// <summary>
|
||||
/// Checks which names the BeyondFacts Lab suggests to remove before the person shares a session.
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
public sealed class BflShareNamesTests
|
||||
{
|
||||
private static readonly DateTimeOffset NOW = new(2026, 11, 12, 9, 0, 0, TimeSpan.Zero);
|
||||
|
||||
[Test]
|
||||
public void TheNamesInTheFieldOfTheOtherParticipantsAreSuggested()
|
||||
{
|
||||
var forms = Forms();
|
||||
forms[BflStep.FRAMEWORK].Set(BflFieldCatalog.OTHER_PARTICIPANTS, "Prof. Dr. Anna Schmidt (TUM)\nModeration: Bernd Weber; Carla von der Heide und Dieter Graf, Bundestag\n- Die Moderatorin Hanna Berg\nVertreter*innen aus der Politik");
|
||||
|
||||
var names = BflShareNames.Suggest(forms, new Dictionary<BflStep, BflWorkState>(), null);
|
||||
Assert.That(names, Is.EqualTo(new[] { "Anna Schmidt", "Bernd Weber", "Carla von der Heide", "Dieter Graf", "Bundestag", "Hanna Berg" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheHeadingsOfTheResearchOnTheParticipantsAreSuggested()
|
||||
{
|
||||
var research = new BflResearch
|
||||
{
|
||||
Text = "## Teilnehmende\n\n### Dr. Eva Klein\n\n- Rolle: Podium\n\n### Frank Ott (Moderation)\n\n## Offene Punkte\n\n- Wer noch?",
|
||||
CreatedAtUtc = NOW,
|
||||
};
|
||||
|
||||
var names = BflShareNames.Suggest(Forms(), new Dictionary<BflStep, BflWorkState>(), research);
|
||||
Assert.That(names, Is.EqualTo(new[] { "Eva Klein", "Frank Ott" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TheNamesInBoldInTheSectionOnTheParticipantsAreSuggested()
|
||||
{
|
||||
var workStates = new Dictionary<BflStep, BflWorkState> { [BflStep.STRATEGY] = new() };
|
||||
workStates[BflStep.STRATEGY].Confirm(new Dictionary<string, string> { ["PARTICIPANTS"] = "- **Gregor Sand**, Moderation, hält das für **wichtig**\n- [**Ida Lux**](https://example.org)" }, new Dictionary<BflStep, int>(), string.Empty, NOW);
|
||||
|
||||
var names = BflShareNames.Suggest(Forms(), workStates, null);
|
||||
Assert.That(names, Is.EqualTo(new[] { "Gregor Sand", "Ida Lux" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ANameFoundTwiceOrAsAPartOfALongerOneIsSuggestedOnce()
|
||||
{
|
||||
var forms = Forms();
|
||||
forms[BflStep.FRAMEWORK].Set(BflFieldCatalog.OTHER_PARTICIPANTS, "Weber\nBernd Weber\nbernd weber");
|
||||
|
||||
var names = BflShareNames.Suggest(forms, new Dictionary<BflStep, BflWorkState>(), null);
|
||||
Assert.That(names, Is.EqualTo(new[] { "Bernd Weber" }));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ALongRunOfCapitalsGivesItsLastTwoWords() => Assert.That(BflShareNames.NamesIn("Neue Forschung Zeigt Lena Graf"), Is.EqualTo(new[] { "Lena Graf" }));
|
||||
|
||||
[Test]
|
||||
public void ADraftOfAWorkStateSuggestsNothing()
|
||||
{
|
||||
var workStates = new Dictionary<BflStep, BflWorkState>
|
||||
{
|
||||
[BflStep.STRATEGY] = new() { Draft = new(StringComparer.Ordinal) { ["PARTICIPANTS"] = "**Gregor Sand**" } },
|
||||
};
|
||||
|
||||
Assert.That(BflShareNames.Suggest(Forms(), workStates, null), Is.Empty);
|
||||
}
|
||||
|
||||
private static Dictionary<BflStep, BflStepFormData> Forms() => BflStep.All.ToDictionary(step => step, _ => new BflStepFormData());
|
||||
}
|
||||
Reference in new issue
Block a user