/// <summary> Adds pronunciation audio of a word to be written out to lift </summary> private void AddAudio(LexEntry entry, Word wordEntry, string path) { foreach (var audioFile in wordEntry.Audio) { var lexPhonetic = new LexPhonetic(); var util = new Utilities(); var projectPath = Path.Combine(util.GenerateFilePath( Utilities.FileType.Dir, true, "", ""), _projectId); projectPath = Path.Combine(projectPath, "Import", "ExtractedLocation"); var extractedDir = Directory.GetDirectories(projectPath); projectPath = Path.Combine(projectPath, extractedDir.Single()); var src = Path.Combine(util.GenerateFilePath( Utilities.FileType.Audio, true), Path.Combine(projectPath, "audio", audioFile)); var dest = Path.Combine(path, audioFile); if (File.Exists(src)) { File.Copy(src, dest, true); var proMultiText = new LiftMultiText { { "href", dest } }; lexPhonetic.MergeIn(MultiText.Create(proMultiText)); entry.Pronunciations.Add(lexPhonetic); } } }
/// <summary> The meat of lift import is done here. This reads in all necessary attributes of a word and adds it to the database. </summary> public async void FinishEntry(LiftEntry entry) { var newWord = new Word(); var proj = _projService.GetProject(_projectId).Result; // Only used when importing semantic domains from a lift-ranges file if (_sdList.Count != 0 && proj.SemanticDomains.Count == 0) { proj.SemanticDomains = _sdList; await _projService.Update(_projectId, proj); } // Add vernacular // TODO: currently we just add the first listed option, we may want to choose eventually if (!entry.CitationForm.IsEmpty) // Prefer citation form for vernacular { newWord.Vernacular = entry.CitationForm.FirstValue.Value.Text; if (proj.VernacularWritingSystem == "") { proj.VernacularWritingSystem = entry.CitationForm.FirstValue.Key; await _projService.Update(_projectId, proj); } } else if (!entry.LexicalForm.IsEmpty) // lexeme form for backup { newWord.Vernacular = entry.LexicalForm.FirstValue.Value.Text; if (proj.VernacularWritingSystem == "") { proj.VernacularWritingSystem = entry.LexicalForm.FirstValue.Key; await _projService.Update(_projectId, proj); } } else // this is not a word if there is no vernacular { return; } // This is not a word if there are no senses if (entry.Senses.Count == 0) { return; } // Add senses newWord.Senses = new List <Sense>(); foreach (var sense in entry.Senses) { var newSense = new Sense { SemanticDomains = new List <SemanticDomain>(), Glosses = new List <Gloss>() }; // Add glosses foreach ((var key, var value) in sense.Gloss) { newSense.Glosses.Add(new Gloss { Language = key, Def = value.Text }); } // Find semantic domains var semanticDomainStrings = new List <string>(); foreach (var trait in sense.Traits) { if (trait.Name.StartsWith("semantic-domain")) { semanticDomainStrings.Add(trait.Value); } } // Add semantic domains foreach (var semanticDomainString in semanticDomainStrings) { // Splits on the space between the number and name of the semantic domain var splitSemDom = semanticDomainString.Split(" ", 2); newSense.SemanticDomains.Add(new SemanticDomain { Id = splitSemDom[0], Name = splitSemDom[1] }); } newWord.Senses.Add(newSense); } // Add plural foreach (var field in entry.Fields) { if (field.Type == "Plural") { foreach (var plural in field.Content) { var PluralForm = entry.Fields.First().Content.First().Value.Text; newWord.Plural = PluralForm; } } } // Get path to dir containing local lift package ~/{projectId}/Import/ExtractedLocation var util = new Utilities(); var importDir = util.GenerateFilePath( Utilities.FileType.Dir, false, "", Path.Combine(_projectId, "Import")); var extractedPathToImport = Path.Combine(importDir, "ExtractedLocation"); // Get path to directory with audio files ~/{projectId}/Import/ExtractedLocation/{liftName}/audio var importListArr = Directory.GetDirectories(extractedPathToImport); var extractedAudioDir = Path.Combine(importListArr.Single(), "audio"); // Only add audio if the files exist if (Directory.Exists(extractedAudioDir)) { // Add audio foreach (var pro in entry.Pronunciations) { // get path to audio file in lift package at // ~/{projectId}/Import/ExtractedLocation/{liftName}/audio/{audioFile}.mp3 var audioFile = pro.Media.First().Url; newWord.Audio.Add(audioFile); } } newWord.ProjectId = _projectId; await _repo.Create(newWord); }
/// <summary> Exports information from a project to a lift package zip </summary> public string LiftExport(string projectId) { // The helper tag must be included because there are also SIL.Utilities. var util = new Utilities(); // Generate the zip dir. var exportDir = util.GenerateFilePath(Utilities.FileType.Dir, true, "", Path.Combine(projectId, "Export")); if (Directory.Exists(Path.Combine(exportDir, "LiftExport"))) { Directory.Delete(Path.Combine(exportDir, "LiftExport"), true); } var zipDir = Path.Combine(exportDir, "LiftExport", "Lift"); Directory.CreateDirectory(zipDir); // Add audio dir inside zip dir. var audioDir = Path.Combine(zipDir, "audio"); Directory.CreateDirectory(audioDir); var liftPath = Path.Combine(zipDir, "NewLiftFile.lift"); // noBOM will work with PrinceXML var liftWriter = new CombineLiftWriter(liftPath, ByteOrderStyle.BOM); var rangesDest = Path.Combine(zipDir, "NewLiftFile.lift-ranges"); // write header of lift document var header = $@" <ranges> <range id = ""semantic-domain-ddp4"" href = ""{rangesDest}""/> </ranges> <fields> <field tag = ""Plural""> <form lang = ""en""><text></text></form> <form lang = ""qaa-x-spec""><text> Class = LexEntry; Type = String; WsSelector = kwsVern </text></form> </field> </fields> "; liftWriter.WriteHeader(header); // Write out every word with all of its information var allWords = _repo.GetAllWords(projectId).Result; var frontier = _repo.GetFrontier(projectId).Result; var activeWords = frontier.Where(x => x.Senses.First().Accessibility == (int)State.Active).ToList(); // TODO: this is wrong, deleted is a subset of active, are not exclusive var deletedWords = allWords.Where(x => activeWords.Contains(x)).ToList(); foreach (var wordEntry in activeWords) { var entry = new LexEntry(); AddVern(entry, wordEntry, projectId); AddSenses(entry, wordEntry); AddAudio(entry, wordEntry, audioDir); liftWriter.Add(entry); } foreach (var wordEntry in deletedWords) { var entry = new LexEntry(); AddVern(entry, wordEntry, projectId); AddSenses(entry, wordEntry); AddAudio(entry, wordEntry, audioDir); liftWriter.AddDeletedEntry(entry); } liftWriter.End(); // Export semantic domains to lift-ranges var proj = _projService.GetProject(projectId).Result; var extractedPathToImport = Path.Combine(GetProjectDir(projectId), "Import", "ExtractedLocation"); var importLiftDir = ""; if (Directory.Exists(extractedPathToImport)) { importLiftDir = Directory.GetDirectories(extractedPathToImport).Select(Path.GetFileName).ToList().Single(); } var rangesSrc = Path.Combine(extractedPathToImport, importLiftDir, $"{importLiftDir}.lift-ranges"); // If there are no new semantic domains, and the old lift-ranges file is still around, just copy it if (proj.SemanticDomains.Count == 0 && File.Exists(rangesSrc)) { File.Copy(rangesSrc, rangesDest, true); } else // Make a new lift-ranges file { var liftRangesWriter = XmlWriter.Create(rangesDest, new XmlWriterSettings { Indent = true, NewLineOnAttributes = true }); liftRangesWriter.WriteStartDocument(); liftRangesWriter.WriteStartElement("lift-ranges"); liftRangesWriter.WriteStartElement("range"); liftRangesWriter.WriteAttributeString("id", "semantic-domain-ddp4"); // Pull from resources file with all English semantic domains var assembly = typeof(LiftService).GetTypeInfo().Assembly; var resource = assembly.GetManifestResourceStream("BackendFramework.Data.sdList.txt"); string sdList; using (var reader = new StreamReader(resource, Encoding.UTF8)) { sdList = reader.ReadToEndAsync().Result; } var sdLines = sdList.Split(Environment.NewLine); foreach (var line in sdLines) { if (line != "") { var items = line.Split("`"); WriteRangeElement(liftRangesWriter, items[0], items[1], items[2], items[3]); } } // Pull from new semantic domains in project foreach (var sd in proj.SemanticDomains) { WriteRangeElement(liftRangesWriter, sd.Id, Guid.NewGuid().ToString(), sd.Name, sd.Description); } liftRangesWriter.WriteEndElement(); //end semantic-domain-ddp4 range liftRangesWriter.WriteEndElement(); //end lift-ranges liftRangesWriter.WriteEndDocument(); liftRangesWriter.Flush(); liftRangesWriter.Close(); } // Export character set to ldml var ldmlDir = Path.Combine(zipDir, "WritingSystems"); Directory.CreateDirectory(ldmlDir); if (proj.VernacularWritingSystem != "") { LdmlExport(ldmlDir, proj.VernacularWritingSystem); } // Compress everything var destinationFileName = Path.Combine(exportDir, Path.Combine($"LiftExportCompressed-{proj.Id}_{string.Format("{0:yyyy-MM-dd_hh-mm-ss}", DateTime.Now)}.zip")); ZipFile.CreateFromDirectory(Path.GetDirectoryName(zipDir), destinationFileName); return(destinationFileName); }