Skip to content

Commit 86fe9c9

Browse files
authored
Implement all LIFT import fields for better protection (#3531)
1 parent 0b714f4 commit 86fe9c9

File tree

7 files changed

+149
-63
lines changed

7 files changed

+149
-63
lines changed

Backend.Tests/Helper/LiftHelperTests.cs

+16
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@ public void EntryUnprotected()
2727
entry.Notes.Add(new("", new("key", "content")));
2828
entry.Pronunciations.Add(new());
2929
entry.Pronunciations.Add(new());
30+
foreach (var pronunciation in entry.Pronunciations)
31+
{
32+
pronunciation.Media.Add(new() { Url = "file://path" });
33+
}
3034
entry.Senses.Add(new());
3135
entry.Senses.Add(new());
3236
// The only entry trait not protected is morph type "stem".
@@ -99,6 +103,18 @@ public void EntryNotesProtected()
99103
Assert.That(reasons.Last().Type, Is.EqualTo(ReasonType.Notes));
100104
}
101105

106+
[Test]
107+
public void EntryPronunciationWithoutUrlProtected()
108+
{
109+
var entry = new LiftEntry();
110+
entry.Pronunciations.Add(new());
111+
entry.Pronunciations.Add(new());
112+
Assert.That(IsProtected(entry), Is.True);
113+
var reasons = GetProtectedReasons(entry);
114+
Assert.That(reasons, Has.Count.EqualTo(1));
115+
Assert.That(reasons.Last().Type, Is.EqualTo(ReasonType.PronunciationWithoutUrl));
116+
}
117+
102118
[Test]
103119
public void EntryRelationsProtected()
104120
{

Backend/Helper/LiftHelper.cs

+7-2
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,9 @@ public static string GetLiftRootFromExtractedZip(string dirPath)
7676
public static bool IsProtected(LiftEntry entry)
7777
{
7878
return entry.Annotations.Count > 0 || entry.Etymologies.Count > 0 || entry.Fields.Count > 0 ||
79-
(entry.Notes.Count == 1 && !string.IsNullOrEmpty(entry.Notes.First().Type)) ||
80-
entry.Notes.Count > 1 || entry.Relations.Count > 0 ||
79+
(entry.Notes.Count == 1 && !string.IsNullOrEmpty(entry.Notes.First().Type)) || entry.Notes.Count > 1 ||
80+
entry.Pronunciations.Any(p => p.Media.All(m => string.IsNullOrEmpty(m.Url))) ||
81+
entry.Relations.Count > 0 ||
8182
entry.Traits.Any(t => !t.Value.Equals("stem", StringComparison.OrdinalIgnoreCase) ||
8283
!t.Name.Replace("-", "").Equals(TraitNames.MorphType, StringComparison.OrdinalIgnoreCase)) ||
8384
entry.Variants.Count > 0;
@@ -107,6 +108,10 @@ public static List<ProtectReason> GetProtectedReasons(LiftEntry entry)
107108
{
108109
reasons.Add(new() { Type = ReasonType.Notes, Count = entry.Notes.Count });
109110
}
111+
if (entry.Pronunciations.Any(p => p.Media.All(m => string.IsNullOrEmpty(m.Url))))
112+
{
113+
reasons.Add(new() { Type = ReasonType.PronunciationWithoutUrl });
114+
}
110115
if (entry.Relations.Count > 0)
111116
{
112117
reasons.Add(new() { Type = ReasonType.Relations, Count = entry.Relations.Count });

Backend/Models/ProtectReason.cs

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public enum ReasonType
1515
Illustrations,
1616
NoteWithType,
1717
Notes,
18+
PronunciationWithoutUrl,
1819
Relations,
1920
Reversals,
2021
Subsenses,

Backend/Services/LiftService.cs

+121-61
Original file line numberDiff line numberDiff line change
@@ -340,7 +340,7 @@ public async Task<string> LiftExport(
340340
if (src is null || !File.Exists(src))
341341
{
342342
continue;
343-
};
343+
}
344344

345345
var safeName = Sanitization.MakeFriendlyForPath(speaker.Name);
346346
var fileName = safeName == "" ? Path.GetFileNameWithoutExtension(src) : safeName;
@@ -549,7 +549,7 @@ private static async Task AddAudio(LexEntry entry, List<Pronunciation> pronuncia
549549
if (!File.Exists(src))
550550
{
551551
continue;
552-
};
552+
}
553553

554554
var dest = Path.Combine(path, audio.FileName);
555555
if (Path.GetExtension(dest).Equals(".webm", StringComparison.OrdinalIgnoreCase))
@@ -865,10 +865,16 @@ public void FinishEntry(LiftEntry entry)
865865
// Only add audio if the files exist
866866
if (Directory.Exists(extractedAudioDir))
867867
{
868-
foreach (var pro in entry.Pronunciations)
868+
foreach (var pronunciation in entry.Pronunciations)
869869
{
870-
// Add audio with Protected = true to prevent modifying or deleting imported audio
871-
newWord.Audio.Add(new Pronunciation(pro.Media.First().Url) { Protected = true });
870+
foreach (var media in pronunciation.Media)
871+
{
872+
if (!string.IsNullOrEmpty(media.Url))
873+
{
874+
// Add audio with Protected = true to prevent modifying or deleting imported audio
875+
newWord.Audio.Add(new(media.Url) { Protected = true });
876+
}
877+
}
872878
}
873879
}
874880

@@ -879,47 +885,44 @@ public void FinishEntry(LiftEntry entry)
879885
/// <summary> Creates the object to transfer all the data from a word </summary>
880886
public LiftEntry GetOrMakeEntry(Extensible info, int order)
881887
{
882-
return new LiftEntry(info, info.Guid, order)
883-
{
884-
LexicalForm = new LiftMultiText(),
885-
CitationForm = new LiftMultiText()
886-
};
888+
return new LiftEntry(info, info.Guid, order) { CitationForm = [], LexicalForm = [] };
887889
}
888890

889891
/// <summary> Creates an empty sense object and adds it to the entry </summary>
890892
public LiftSense GetOrMakeSense(LiftEntry entry, Extensible info, string rawXml)
891893
{
892-
var sense = new LiftSense(info, info.Guid, entry)
893-
{
894-
Definition = new LiftMultiText(),
895-
Gloss = new LiftMultiText()
896-
};
894+
var sense = new LiftSense(info, info.Guid, entry) { Definition = [], Gloss = [] };
897895
entry.Senses.Add(sense);
898896
return sense;
899897
}
900898

901899
/// <summary> Adds each citation form to the entry for the vernacular </summary>
902900
public void MergeInCitationForm(LiftEntry entry, LiftMultiText contents)
903901
{
902+
entry.CitationForm ??= [];
904903
foreach (var (key, value) in contents)
905904
{
906905
entry.CitationForm.Add(key, value.Text);
907906
}
908907
}
909908

910909
/// <summary> Adds field to the entry for plural forms </summary>
911-
public void MergeInField(LiftObject extensible, string typeAttribute, DateTime dateCreated,
910+
public void MergeInField(LiftObject extensible, string tagAttribute, DateTime dateCreated,
912911
DateTime dateModified, LiftMultiText contents, List<Trait> traits)
913912
{
914-
var textEntry = new LiftMultiText(contents.FirstValue.Key,
915-
contents.FirstValue.Value.Text);
916-
var fieldEntry = new LiftField(typeAttribute, textEntry);
917-
extensible.Fields.Add(fieldEntry);
913+
var field = new LiftField(tagAttribute, contents)
914+
{
915+
DateCreated = dateCreated,
916+
DateModified = dateModified,
917+
};
918+
field.Traits.AddRange(traits.Select(t => new LiftTrait() { Name = t.Name, Value = t.Value }));
919+
extensible.Fields.Add(field);
918920
}
919921

920922
/// <summary> Adds sense's definitions to the entry. </summary>
921923
public void MergeInDefinition(LiftSense sense, LiftMultiText multiText)
922924
{
925+
sense.Definition ??= [];
923926
foreach (var (key, value) in multiText)
924927
{
925928
sense.Definition.Add(key, value.Text);
@@ -929,6 +932,7 @@ public void MergeInDefinition(LiftSense sense, LiftMultiText multiText)
929932
/// <summary> Adds sense's glosses to the entry. </summary>
930933
public void MergeInGloss(LiftSense sense, LiftMultiText multiText)
931934
{
935+
sense.Gloss ??= [];
932936
foreach (var (key, value) in multiText)
933937
{
934938
sense.Gloss.Add(key, value.Text);
@@ -938,6 +942,7 @@ public void MergeInGloss(LiftSense sense, LiftMultiText multiText)
938942
/// <summary> Adds each lexeme form to the entry for the vernacular </summary>
939943
public void MergeInLexemeForm(LiftEntry entry, LiftMultiText contents)
940944
{
945+
entry.LexicalForm ??= [];
941946
foreach (var (key, value) in contents)
942947
{
943948
entry.LexicalForm.Add(key, value);
@@ -947,50 +952,52 @@ public void MergeInLexemeForm(LiftEntry entry, LiftMultiText contents)
947952
/// <summary> Adds field to the entry for semantic domains </summary>
948953
public void MergeInTrait(LiftObject extensible, Trait trait)
949954
{
950-
extensible.Traits.Add(new LiftTrait { Name = trait.Name, Value = trait.Value });
955+
extensible.Traits.Add(new() { Name = trait.Name, Value = trait.Value });
951956
}
952957

953958
/// <summary> Needs to be called before MergeInMedia </summary>
954959
public LiftObject MergeInPronunciation(LiftEntry entry, LiftMultiText contents, string rawXml)
955960
{
956-
return entry;
961+
var pronunciation = new LiftPhonetic { Form = contents };
962+
entry.Pronunciations.Add(pronunciation);
963+
return pronunciation;
957964
}
958965

959966
/// <summary> Adds in media for audio pronunciation </summary>
960967
public void MergeInMedia(LiftObject pronunciation, string href, LiftMultiText caption)
961968
{
962-
var entry = (LiftEntry)pronunciation;
963-
var phonetic = new LiftPhonetic();
964-
var url = new LiftUrlRef { Url = href, Label = caption };
965-
phonetic.Media.Add(url);
966-
entry.Pronunciations.Add(phonetic);
969+
(pronunciation as LiftPhonetic)?.Media.Add(new() { Label = caption, Url = href });
967970
}
968971

969972
/// <summary> Adds in note, if there is one to add </summary>
970973
public void MergeInNote(LiftObject extensible, string type, LiftMultiText contents, string rawXml)
971974
{
975+
var note = new LiftNote(type, contents);
972976
if (extensible is LiftEntry entry)
973977
{
974-
var note = new LiftNote(
975-
// This application only uses "basic" notes, which have no type
976-
null,
977-
new LiftMultiText(contents.FirstValue.Key, contents.FirstValue.Value.Text));
978978
entry.Notes.Add(note);
979979
}
980+
else if (extensible is LiftSense sense)
981+
{
982+
sense.Notes.Add(note);
983+
}
984+
else if (extensible is LiftExample example)
985+
{
986+
example.Notes.Add(note);
987+
}
980988
}
981989

982-
public void MergeInGrammaticalInfo(LiftObject senseOrReversal, string val, List<Trait> traits)
990+
public void MergeInGrammaticalInfo(LiftObject obj, string val, List<Trait> traits)
983991
{
984-
if (senseOrReversal is LiftSense sense)
992+
var gramInfo = new LiftGrammaticalInfo { Value = val };
993+
gramInfo.Traits.AddRange(traits.Select(t => new LiftTrait { Name = t.Name, Value = t.Value }));
994+
if (obj is LiftSense sense)
985995
{
986-
if (sense.GramInfo is null)
987-
{
988-
sense.GramInfo = new LiftGrammaticalInfo { Value = val };
989-
}
990-
else
991-
{
992-
sense.GramInfo.Value = val;
993-
}
996+
sense.GramInfo = gramInfo;
997+
}
998+
else if (obj is LiftReversal reversal)
999+
{
1000+
reversal.GramInfo = gramInfo;
9941001
}
9951002
}
9961003

@@ -1024,60 +1031,113 @@ public void ProcessRangeElement(string range, string id, string guid, string par
10241031
}
10251032
}
10261033

1027-
// The following are unused and are not implemented, but may still be called by the Lexicon Merger
1028-
// They may be useful later if we need to add more complex attributes to words in The Combine
1034+
// The following may be called by the Lexicon Merger.
1035+
// We don't use this info in The Combine except to know when to protect imported data.
10291036
[ExcludeFromCodeCoverage]
10301037
public LiftExample GetOrMakeExample(LiftSense sense, Extensible info)
10311038
{
1032-
return new LiftExample { Content = new LiftMultiText() };
1039+
var example = new LiftExample
1040+
{
1041+
DateCreated = info.CreationTime,
1042+
DateModified = info.ModificationTime,
1043+
Guid = info.Guid,
1044+
Id = info.Id
1045+
};
1046+
sense.Examples.Add(example);
1047+
return example;
10331048
}
1049+
10341050
[ExcludeFromCodeCoverage]
10351051
public LiftObject GetOrMakeParentReversal(LiftObject parent, LiftMultiText contents, string type)
10361052
{
1037-
return new LiftReversal();
1053+
return new LiftReversal { Form = contents, Main = parent as LiftReversal, Type = type };
10381054
}
1055+
10391056
[ExcludeFromCodeCoverage]
10401057
public LiftSense GetOrMakeSubsense(LiftSense sense, Extensible info, string rawXml)
10411058
{
1042-
return new LiftSense(info, new Guid(), sense)
1043-
{
1044-
Definition = new LiftMultiText(),
1045-
Gloss = new LiftMultiText()
1046-
};
1059+
var subsense = new LiftSense(info, info.Guid, sense);
1060+
sense.Subsenses.Add(subsense);
1061+
return subsense;
10471062
}
1063+
10481064
[ExcludeFromCodeCoverage]
10491065
public LiftObject MergeInEtymology(LiftEntry entry, string source, string type, LiftMultiText form,
10501066
LiftMultiText gloss, string rawXml)
10511067
{
1052-
return new LiftEtymology();
1068+
var etymology = new LiftEtymology { Form = form, Gloss = gloss, Source = source, Type = type };
1069+
entry.Etymologies.Add(etymology);
1070+
return etymology;
10531071
}
1072+
10541073
[ExcludeFromCodeCoverage]
10551074
public LiftObject MergeInReversal(
10561075
LiftSense sense, LiftObject parent, LiftMultiText contents, string type, string rawXml)
10571076
{
1058-
return new LiftReversal();
1077+
var reversal = new LiftReversal { Form = contents, Main = parent as LiftReversal, Type = type };
1078+
sense.Reversals.Add(reversal);
1079+
return reversal;
10591080
}
1081+
10601082
[ExcludeFromCodeCoverage]
10611083
public LiftObject MergeInVariant(LiftEntry entry, LiftMultiText contents, string rawXml)
10621084
{
1063-
return new LiftVariant();
1085+
var variant = new LiftVariant { Form = contents, RawXml = rawXml };
1086+
entry.Variants.Add(variant);
1087+
return variant;
10641088
}
1089+
10651090
[ExcludeFromCodeCoverage]
1066-
public void EntryWasDeleted(Extensible info, DateTime dateDeleted) { }
1067-
[ExcludeFromCodeCoverage]
1068-
public void MergeInExampleForm(LiftExample example, LiftMultiText multiText) { }
1091+
public void MergeInExampleForm(LiftExample example, LiftMultiText multiText)
1092+
{
1093+
example.Content ??= [];
1094+
foreach (var (key, value) in multiText)
1095+
{
1096+
example.Content.Add(key, value);
1097+
}
1098+
}
1099+
10691100
[ExcludeFromCodeCoverage]
1070-
public void MergeInPicture(LiftSense sense, string href, LiftMultiText caption) { }
1101+
public void MergeInPicture(LiftSense sense, string href, LiftMultiText caption)
1102+
{
1103+
sense.Illustrations.Add(new() { Label = caption, Url = href });
1104+
}
1105+
10711106
[ExcludeFromCodeCoverage]
10721107
public void MergeInRelation(
10731108
LiftObject extensible, string relationTypeName, string targetId, string rawXml)
1074-
{ }
1109+
{
1110+
var relation = new LiftRelation { Ref = targetId, Type = relationTypeName };
1111+
if (extensible is LiftEntry entry)
1112+
{
1113+
entry.Relations.Add(relation);
1114+
}
1115+
else if (extensible is LiftSense sense)
1116+
{
1117+
sense.Relations.Add(relation);
1118+
}
1119+
else if (extensible is LiftVariant variant)
1120+
{
1121+
variant.Relations.Add(relation);
1122+
}
1123+
}
1124+
10751125
[ExcludeFromCodeCoverage]
1076-
public void MergeInSource(LiftExample example, string source) { }
1126+
public void MergeInSource(LiftExample example, string source)
1127+
{
1128+
example.Source = source;
1129+
}
1130+
10771131
[ExcludeFromCodeCoverage]
10781132
public void MergeInTranslationForm(
1079-
LiftExample example, string type, LiftMultiText multiText, string rawXml)
1080-
{ }
1133+
LiftExample example, string type, LiftMultiText contents, string rawXml)
1134+
{
1135+
example.Translations.Add(new() { Content = contents, Type = type });
1136+
}
1137+
1138+
// The following are unimplemented, but may still be called by the Lexicon Merger
1139+
[ExcludeFromCodeCoverage]
1140+
public void EntryWasDeleted(Extensible info, DateTime dateDeleted) { }
10811141
[ExcludeFromCodeCoverage]
10821142
public void ProcessFieldDefinition(string tag, LiftMultiText description) { }
10831143
}

public/locales/en/translation.json

+1
Original file line numberDiff line numberDiff line change
@@ -429,6 +429,7 @@
429429
"noteWithType": "note with type \"{{ val }}\"",
430430
"notesSense": "notes",
431431
"notesWord": "more than 1 note",
432+
"pronunciationWithoutUrl": "pronunciation without a media file",
432433
"relations": "relations",
433434
"reversal": "\"{{ val }}\" reversal",
434435
"subsenses": "subsenses",

src/api/models/reason-type.ts

+1
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ export enum ReasonType {
2626
Illustrations = "Illustrations",
2727
NoteWithType = "NoteWithType",
2828
Notes = "Notes",
29+
PronunciationWithoutUrl = "PronunciationWithoutUrl",
2930
Relations = "Relations",
3031
Reversals = "Reversals",
3132
Subsenses = "Subsenses",

0 commit comments

Comments
 (0)