public static async Task <GenerationReturn> CopyCleanResizeImage(ImageContent dbEntry,
                                                                         IProgress <string> progress)
        {
            if (dbEntry == null)
            {
                return(await GenerationReturn.Error("Null Image Content submitted to Copy Clean and Resize"));
            }

            progress?.Report($"Starting Copy, Clean and Resize for {dbEntry.Title}");

            if (string.IsNullOrWhiteSpace(dbEntry.OriginalFileName))
            {
                return(await GenerationReturn.Error($"Image {dbEntry.Title} has no Original File", dbEntry.ContentId));
            }

            var imageDirectory = UserSettingsSingleton.CurrentSettings().LocalSiteImageContentDirectory(dbEntry);

            var syncCopyResults = await FileManagement.CheckImageFileIsInMediaAndContentDirectories(dbEntry, progress);

            if (!syncCopyResults.HasError)
            {
                return(syncCopyResults);
            }

            CleanDisplayAndSrcSetFilesInImageDirectory(dbEntry, true, progress);

            ResizeForDisplayAndSrcset(new FileInfo(Path.Combine(imageDirectory.FullName, dbEntry.OriginalFileName)),
                                      false, progress);

            return(await GenerationReturn.Success($"{dbEntry.Title} Copied, Cleaned, Resized", dbEntry.ContentId));
        }
        public static async Task <(GenerationReturn generationReturn, TagExclusion returnContent)> Save(
            TagExclusion toSave)
        {
            var validationResult = await Validate(toSave);

            if (validationResult.HasError)
            {
                return(validationResult, null);
            }

            var db = await Db.Context();

            if (toSave.Id < 1)
            {
                toSave.Tag            = Db.TagListItemCleanup(toSave.Tag);
                toSave.ContentVersion = DateTime.Now.ToUniversalTime().TrimDateTimeToSeconds();

                await db.AddAsync(toSave);

                await db.SaveChangesAsync(true);

                return(await GenerationReturn.Success("Tag Exclusion Saved"), toSave);
            }

            var toModify = await db.TagExclusions.SingleAsync(x => x.Id == toSave.Id);

            toModify.Tag            = Db.TagListItemCleanup(toSave.Tag);
            toModify.ContentVersion = DateTime.Now.ToUniversalTime().TrimDateTimeToSeconds();

            await db.SaveChangesAsync(true);

            return(await GenerationReturn.Success("Tag Exclusion Saved"), toModify);
        }
        public static async Task <GenerationReturn> Validate(TagExclusion toValidate)
        {
            if (toValidate == null)
            {
                return(await GenerationReturn.Error("Excluded Tag can not be empty"));
            }

            if (string.IsNullOrWhiteSpace(toValidate.Tag))
            {
                return(await GenerationReturn.Error("Excluded Tag can not be blank"));
            }

            var validationResult = CommonContentValidation.ValidateTags(toValidate.Tag.TrimNullToEmpty());

            if (!validationResult.isValid)
            {
                return(await GenerationReturn.Error(validationResult.explanation));
            }

            var cleanedTag = Db.TagListItemCleanup(toValidate.Tag);

            var db = await Db.Context();

            if (db.TagExclusions.Any(x => x.Id != toValidate.Id && x.Tag == cleanedTag))
            {
                return(await GenerationReturn.Error(
                           $"It appears that the tag '{cleanedTag}' is already in the Exclusion List?"));
            }

            return(await GenerationReturn.Success("Tag Exclusion is Valid"));
        }
Пример #4
0
        public static async Task <GenerationReturn> Validate(LinkContent linkContent)
        {
            var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory();

            if (!rootDirectoryCheck.Item1)
            {
                return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}",
                                                    linkContent.ContentId));
            }

            if (linkContent == null)
            {
                return(await GenerationReturn.Error("Link Content is Null?"));
            }

            var(createdUpdatedValid, createdUpdatedValidationMessage) =
                CommonContentValidation.ValidateCreatedAndUpdatedBy(linkContent, linkContent.Id < 1);

            if (!createdUpdatedValid)
            {
                return(await GenerationReturn.Error(createdUpdatedValidationMessage, linkContent.ContentId));
            }

            var urlValidation =
                await CommonContentValidation.ValidateLinkContentLinkUrl(linkContent.Url, linkContent.ContentId);

            if (!urlValidation.isValid)
            {
                return(await GenerationReturn.Error(urlValidation.explanation, linkContent.ContentId));
            }

            return(await GenerationReturn.Success("Link Content Validation Successful"));
        }
Пример #5
0
        public static async Task <(GenerationReturn generationReturn, MapComponentDto mapDto)> SaveAndGenerateData(
            MapComponentDto toSave, DateTime?generationVersion, IProgress <string> progress)
        {
            var validationReturn = await Validate(toSave);

            if (validationReturn.HasError)
            {
                return(validationReturn, null);
            }

            Db.DefaultPropertyCleanup(toSave);

            var savedComponent = await Db.SaveMapComponent(toSave);

            await GenerateData(savedComponent, progress);

            await Export.WriteLocalDbJson(savedComponent.Map);

            DataNotifications.PublishDataNotification("Map Component Generator", DataNotificationContentType.Map,
                                                      DataNotificationUpdateType.LocalContent, new List <Guid> {
                savedComponent.Map.ContentId
            });

            return(
                await GenerationReturn.Success(
                    $"Saved and Generated Map Component {savedComponent.Map.ContentId} - {savedComponent.Map.Title}"),
                savedComponent);
        }
Пример #6
0
        public static async Task <GenerationReturn> Validate(MapComponentDto mapComponent)
        {
            var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory();

            if (!rootDirectoryCheck.Item1)
            {
                return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}",
                                                    mapComponent.Map.ContentId));
            }

            var commonContentCheck = await CommonContentValidation.ValidateMapComponent(mapComponent);

            if (!commonContentCheck.valid)
            {
                return(await GenerationReturn.Error(commonContentCheck.explanation, mapComponent.Map.ContentId));
            }

            var updateFormatCheck =
                CommonContentValidation.ValidateUpdateContentFormat(mapComponent.Map.UpdateNotesFormat);

            if (!updateFormatCheck.isValid)
            {
                return(await GenerationReturn.Error(updateFormatCheck.explanation, mapComponent.Map.ContentId));
            }

            return(await GenerationReturn.Success("GeoJson Content Validation Successful"));
        }
Пример #7
0
        public static async Task <(GenerationReturn generationReturn, LinkContent linkContent)> SaveAndGenerateHtml(
            LinkContent toSave, DateTime?generationVersion, IProgress <string> progress)
        {
            var validationReturn = await Validate(toSave);

            if (validationReturn.HasError)
            {
                return(validationReturn, null);
            }

            Db.DefaultPropertyCleanup(toSave);
            toSave.Tags = Db.TagListCleanup(toSave.Tags);

            await Db.SaveLinkContent(toSave);

            await SaveLinkToPinboard(toSave, progress);

            GenerateHtmlAndJson(generationVersion, progress);

            DataNotifications.PublishDataNotification("Link Generator", DataNotificationContentType.Link,
                                                      DataNotificationUpdateType.LocalContent, new List <Guid> {
                toSave.ContentId
            });

            return(
                await GenerationReturn.Success($"Saved and Generated Content And Html for Links to Add {toSave.Title}"),
                toSave);
        }
Пример #8
0
        public static async Task <(GenerationReturn generationReturn, PointContentDto pointContent)> SaveAndGenerateHtml(
            PointContentDto toSave, DateTime?generationVersion, IProgress <string> progress)
        {
            var validationReturn = await Validate(toSave);

            if (validationReturn.HasError)
            {
                return(validationReturn, null);
            }

            Db.DefaultPropertyCleanup(toSave);
            toSave.Tags = Db.TagListCleanup(toSave.Tags);

            var savedPoint = await Db.SavePointContent(toSave);

            GenerateHtml(savedPoint, generationVersion, progress);
            await Export.WriteLocalDbJson(Db.PointContentDtoToPointContentAndDetails(savedPoint).content);

            DataNotifications.PublishDataNotification("Point Generator", DataNotificationContentType.Point,
                                                      DataNotificationUpdateType.LocalContent, new List <Guid> {
                savedPoint.ContentId
            });

            return(await GenerationReturn.Success($"Saved and Generated Content And Html for {savedPoint.Title}"),
                   savedPoint);
        }
Пример #9
0
        public static async Task <(GenerationReturn generationReturn, NoteContent noteContent)> SaveAndGenerateHtml(
            NoteContent toSave, DateTime?generationVersion, IProgress <string> progress)
        {
            var validationReturn = await Validate(toSave);

            if (validationReturn.HasError)
            {
                return(validationReturn, null);
            }

            Db.DefaultPropertyCleanup(toSave);
            toSave.Tags = Db.TagListCleanup(toSave.Tags);

            await Db.SaveNoteContent(toSave);

            GenerateHtml(toSave, generationVersion, progress);
            await Export.WriteLocalDbJson(toSave, progress);

            DataNotifications.PublishDataNotification("Note Generator", DataNotificationContentType.Note,
                                                      DataNotificationUpdateType.LocalContent, new List <Guid> {
                toSave.ContentId
            });

            return(await GenerationReturn.Success($"Saved and Generated Content And Html for {toSave.Title}"), toSave);
        }
Пример #10
0
        public static async Task <(GenerationReturn generationReturn, FileContent fileContent)> SaveAndGenerateHtml(
            FileContent toSave, FileInfo selectedFile, bool overwriteExistingFiles, DateTime?generationVersion,
            IProgress <string> progress)
        {
            var validationReturn = await Validate(toSave, selectedFile);

            if (validationReturn.HasError)
            {
                return(validationReturn, null);
            }

            Db.DefaultPropertyCleanup(toSave);
            toSave.Tags = Db.TagListCleanup(toSave.Tags);

            toSave.OriginalFileName = selectedFile.Name;
            FileManagement.WriteSelectedFileContentFileToMediaArchive(selectedFile);
            await Db.SaveFileContent(toSave);

            WriteFileFromMediaArchiveToLocalSite(toSave, overwriteExistingFiles, progress);
            GenerateHtml(toSave, generationVersion, progress);
            await Export.WriteLocalDbJson(toSave, progress);

            DataNotifications.PublishDataNotification("File Generator", DataNotificationContentType.File,
                                                      DataNotificationUpdateType.LocalContent, new List <Guid> {
                toSave.ContentId
            });

            return(await GenerationReturn.Success($"Saved and Generated Content And Html for {toSave.Title}"), toSave);
        }
Пример #11
0
        public static async Task <GenerationReturn> Validate(ImageContent imageContent, FileInfo selectedFile)
        {
            var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory();

            if (!rootDirectoryCheck.Item1)
            {
                return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}",
                                                    imageContent.ContentId));
            }

            var mediaArchiveCheck = UserSettingsUtilities.ValidateLocalMediaArchive();

            if (!mediaArchiveCheck.Item1)
            {
                return(await GenerationReturn.Error($"Problem with Media Archive: {mediaArchiveCheck.Item2}",
                                                    imageContent.ContentId));
            }

            var commonContentCheck = await CommonContentValidation.ValidateContentCommon(imageContent);

            if (!commonContentCheck.valid)
            {
                return(await GenerationReturn.Error(commonContentCheck.explanation, imageContent.ContentId));
            }

            var updateFormatCheck = CommonContentValidation.ValidateUpdateContentFormat(imageContent.UpdateNotesFormat);

            if (!updateFormatCheck.isValid)
            {
                return(await GenerationReturn.Error(updateFormatCheck.explanation, imageContent.ContentId));
            }

            selectedFile.Refresh();

            if (!selectedFile.Exists)
            {
                return(await GenerationReturn.Error("Selected File doesn't exist?", imageContent.ContentId));
            }

            if (!FolderFileUtility.IsNoUrlEncodingNeeded(Path.GetFileNameWithoutExtension(selectedFile.Name)))
            {
                return(await GenerationReturn.Error("Limit File Names to A-Z a-z - . _", imageContent.ContentId));
            }

            if (!FolderFileUtility.PictureFileTypeIsSupported(selectedFile))
            {
                return(await GenerationReturn.Error("The file doesn't appear to be a supported file type.",
                                                    imageContent.ContentId));
            }

            if (await(await Db.Context()).ImageFilenameExistsInDatabase(selectedFile.Name, imageContent.ContentId))
            {
                return(await GenerationReturn.Error(
                           "This filename already exists in the database - image file names must be unique.",
                           imageContent.ContentId));
            }

            return(await GenerationReturn.Success("Image Content Validation Successful"));
        }
Пример #12
0
        public static async Task <(GenerationReturn, PhotoContent)> PhotoMetadataToNewPhotoContent(FileInfo selectedFile,
                                                                                                   IProgress <string> progress, string photoContentCreatedBy = null)
        {
            selectedFile.Refresh();

            if (!selectedFile.Exists)
            {
                return(await GenerationReturn.Error("File Does Not Exist?"), null);
            }

            var metadataReturn = await PhotoMetadataFromFile(selectedFile, progress);

            if (metadataReturn.generationReturn.HasError)
            {
                return(metadataReturn.generationReturn, null);
            }

            var toReturn = new PhotoContent();

            toReturn.InjectFrom(metadataReturn.metadata);

            toReturn.OriginalFileName = selectedFile.Name;
            toReturn.ContentId        = Guid.NewGuid();
            toReturn.CreatedBy        = string.IsNullOrWhiteSpace(photoContentCreatedBy)
                ? UserSettingsSingleton.CurrentSettings().DefaultCreatedBy
                : photoContentCreatedBy.Trim();
            toReturn.CreatedOn         = DateTime.Now;
            toReturn.Slug              = SlugUtility.Create(true, toReturn.Title);
            toReturn.BodyContentFormat = ContentFormatDefaults.Content.ToString();
            toReturn.UpdateNotesFormat = ContentFormatDefaults.Content.ToString();

            var possibleTitleYear = Regex
                                    .Match(toReturn.Title,
                                           @"\A(?<possibleYear>\d\d\d\d) (?<possibleMonth>January?|February?|March?|April?|May|June?|July?|August?|September?|October?|November?|December?) .*",
                                           RegexOptions.IgnoreCase).Groups["possibleYear"].Value;

            if (!string.IsNullOrWhiteSpace(possibleTitleYear))
            {
                if (int.TryParse(possibleTitleYear, out var convertedYear))
                {
                    if (convertedYear >= 1826 && convertedYear <= DateTime.Now.Year)
                    {
                        toReturn.Folder = convertedYear.ToString("F0");
                    }
                }
            }

            if (string.IsNullOrWhiteSpace(toReturn.Folder))
            {
                toReturn.Folder = toReturn.PhotoCreatedOn.Year.ToString("F0");
            }

            return(await GenerationReturn.Success($"Parsed Photo Metadata for {selectedFile.FullName} without error"),
                   toReturn);
        }
Пример #13
0
        public static async Task <(GenerationReturn generationReturn, PhotoContent photoContent)> SaveToDb(
            PhotoContent toSave, FileInfo selectedFile, IProgress <string> progress)
        {
            var validationReturn = await Validate(toSave, selectedFile);

            if (validationReturn.HasError)
            {
                return(validationReturn, null);
            }

            FileManagement.WriteSelectedPhotoContentFileToMediaArchive(selectedFile);
            await Db.SavePhotoContent(toSave);

            return(await GenerationReturn.Success($"Saved {toSave.Title}"), toSave);
        }
Пример #14
0
        public static async Task <GenerationReturn> Validate(PhotoContent photoContent, FileInfo selectedFile)
        {
            var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory();

            if (!rootDirectoryCheck.Item1)
            {
                return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}",
                                                    photoContent.ContentId));
            }

            var mediaArchiveCheck = UserSettingsUtilities.ValidateLocalMediaArchive();

            if (!mediaArchiveCheck.Item1)
            {
                return(await GenerationReturn.Error($"Problem with Media Archive: {mediaArchiveCheck.Item2}",
                                                    photoContent.ContentId));
            }

            var commonContentCheck = await CommonContentValidation.ValidateContentCommon(photoContent);

            if (!commonContentCheck.valid)
            {
                return(await GenerationReturn.Error(commonContentCheck.explanation, photoContent.ContentId));
            }

            var updateFormatCheck = CommonContentValidation.ValidateUpdateContentFormat(photoContent.UpdateNotesFormat);

            if (!updateFormatCheck.isValid)
            {
                return(await GenerationReturn.Error(updateFormatCheck.explanation, photoContent.ContentId));
            }

            selectedFile.Refresh();

            var photoFileValidation =
                await CommonContentValidation.PhotoFileValidation(selectedFile, photoContent?.ContentId);

            if (!photoFileValidation.isValid)
            {
                return(await GenerationReturn.Error(photoFileValidation.explanation, photoContent.ContentId));
            }

            return(await GenerationReturn.Success("Photo Content Validation Successful"));
        }
Пример #15
0
        public static async Task <GenerationReturn> Validate(NoteContent noteContent)
        {
            var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory();

            if (!rootDirectoryCheck.Item1)
            {
                return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}",
                                                    noteContent.ContentId));
            }

            var commonContentCheck = await CommonContentValidation.ValidateContentCommon(noteContent);

            if (!commonContentCheck.valid)
            {
                return(await GenerationReturn.Error(commonContentCheck.explanation, noteContent.ContentId));
            }

            return(await GenerationReturn.Success("Note Content Validation Successful"));
        }
Пример #16
0
        public static async Task <GenerationReturn> CheckStringForBadContentReferences(string toSearch,
                                                                                       PointlessWaymarksContext db, IProgress <string> progress)
        {
            var extracted = new List <Guid>();

            if (string.IsNullOrWhiteSpace(toSearch))
            {
                return(await GenerationReturn.Success("No Content Ids Found"));
            }

            extracted.AddRange(BracketCodeCommon.BracketCodeContentIds(toSearch));

            if (!extracted.Any())
            {
                return(await GenerationReturn.Success("No Content Ids Found"));
            }

            progress?.Report($"Found {extracted.Count} ContentIds to check for");

            var notFoundList = new List <Guid>();

            foreach (var loopExtracted in extracted)
            {
                var contentLookup = await db.ContentFromContentId(loopExtracted);

                if (contentLookup == null)
                {
                    progress?.Report($"ContentId {loopExtracted} Not Found in Db...");
                    notFoundList.Add(loopExtracted);
                }
            }

            if (notFoundList.Any())
            {
                return(await GenerationReturn.Error(
                           $"Invalid ContentIds in Bracket Codes - {string.Join(", ", notFoundList)}"));
            }

            return(await GenerationReturn.Success("No Invalid Content Ids Found"));
        }
Пример #17
0
        public static async Task <(GenerationReturn generationReturn, LinkMetadata metadata)> LinkMetadataFromUrl(
            string url, IProgress <string> progress)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                return(await GenerationReturn.Error("No URL?"), null);
            }

            progress?.Report("Setting up and Downloading Site");

            var toReturn = new LinkMetadata();

            var config   = Configuration.Default.WithDefaultLoader().WithJs();
            var context  = BrowsingContext.New(config);
            var document = await context.OpenAsync(url);

            progress?.Report("Looking for Title");

            var titleString = document.Head?.Children.FirstOrDefault(x => x.TagName == "TITLE")?.TextContent;

            if (string.IsNullOrWhiteSpace(titleString))
            {
                titleString = document.QuerySelector("meta[property='og:title']")?.Attributes
                              .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            if (string.IsNullOrWhiteSpace(titleString))
            {
                titleString = document.QuerySelector("meta[name='DC.title']")?.Attributes
                              .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            if (string.IsNullOrWhiteSpace(titleString))
            {
                titleString = document.QuerySelector("meta[name='twitter:title']")?.Attributes
                              .FirstOrDefault(x => x.LocalName == "value")?.Value;
            }

            if (!string.IsNullOrWhiteSpace(titleString))
            {
                toReturn.Title = titleString;
            }

            progress?.Report("Looking for Author");

            var authorString = document.QuerySelector("meta[property='og:author']")?.Attributes
                               .FirstOrDefault(x => x.LocalName == "content")?.Value;

            if (string.IsNullOrWhiteSpace(authorString))
            {
                authorString = document.QuerySelector("meta[name='DC.contributor']")?.Attributes
                               .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            if (string.IsNullOrWhiteSpace(authorString))
            {
                authorString = document.QuerySelector("meta[property='article:author']")?.Attributes
                               .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            if (string.IsNullOrWhiteSpace(authorString))
            {
                authorString = document.QuerySelector("meta[name='author']")?.Attributes
                               .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            if (string.IsNullOrWhiteSpace(authorString))
            {
                authorString = document.QuerySelector("a[rel~=\"author\"]")?.TextContent;
            }

            if (string.IsNullOrWhiteSpace(authorString))
            {
                authorString = document.QuerySelector(".author__name")?.TextContent;
            }

            if (string.IsNullOrWhiteSpace(authorString))
            {
                authorString = document.QuerySelector(".author_name")?.TextContent;
            }

            if (!string.IsNullOrWhiteSpace(authorString))
            {
                toReturn.Author = authorString;
            }

            progress?.Report($"Looking for Author - Found {toReturn.Author}");


            progress?.Report("Looking for Date Time");

            var linkDateString = document.QuerySelector("meta[property='article:modified_time']")?.Attributes
                                 .FirstOrDefault(x => x.LocalName == "content")?.Value;

            if (string.IsNullOrWhiteSpace(linkDateString))
            {
                linkDateString = document.QuerySelector("meta[property='og:updated_time']")?.Attributes
                                 .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            if (string.IsNullOrWhiteSpace(linkDateString))
            {
                linkDateString = document.QuerySelector("meta[property='article:published_time']")?.Attributes
                                 .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            if (string.IsNullOrWhiteSpace(linkDateString))
            {
                linkDateString = document.QuerySelector("meta[property='article:published_time']")?.Attributes
                                 .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            if (string.IsNullOrWhiteSpace(linkDateString))
            {
                linkDateString = document.QuerySelector("meta[name='DC.date.created']")?.Attributes
                                 .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            progress?.Report($"Looking for Date Time - Found {linkDateString}");

            if (!string.IsNullOrWhiteSpace(linkDateString))
            {
                if (DateTime.TryParse(linkDateString, out var parsedDateTime))
                {
                    toReturn.LinkDate = parsedDateTime;
                    progress?.Report($"Looking for Date Time - Parsed to {parsedDateTime}");
                }
                else
                {
                    progress?.Report("Did not parse Date Time");
                }
            }

            progress?.Report("Looking for Site Name");

            var siteString = document.QuerySelector("meta[property='og:site_name']")?.Attributes
                             .FirstOrDefault(x => x.LocalName == "content")?.Value;

            if (string.IsNullOrWhiteSpace(siteString))
            {
                siteString = document.QuerySelector("meta[name='DC.publisher']")?.Attributes
                             .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            if (string.IsNullOrWhiteSpace(siteString))
            {
                siteString = document.QuerySelector("meta[name='twitter:site']")?.Attributes
                             .FirstOrDefault(x => x.LocalName == "value")?.Value.Replace("@", "");
            }

            if (!string.IsNullOrWhiteSpace(siteString))
            {
                toReturn.Site = siteString;
            }

            progress?.Report($"Looking for Site Name - Found {toReturn.Site}");

            progress?.Report("Looking for Description");

            var descriptionString = document.QuerySelector("meta[name='description']")?.Attributes
                                    .FirstOrDefault(x => x.LocalName == "content")?.Value;

            if (string.IsNullOrWhiteSpace(descriptionString))
            {
                descriptionString = document.QuerySelector("meta[property='og:description']")?.Attributes
                                    .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            if (string.IsNullOrWhiteSpace(descriptionString))
            {
                descriptionString = document.QuerySelector("meta[name='twitter:description']")?.Attributes
                                    .FirstOrDefault(x => x.LocalName == "content")?.Value;
            }

            if (!string.IsNullOrWhiteSpace(descriptionString))
            {
                toReturn.Description = descriptionString;
            }

            progress?.Report($"Looking for Description - Found {toReturn.Description}");

            return(await GenerationReturn.Success($"Parsed URL Metadata for {url} without error"), toReturn);
        }
Пример #18
0
        public static async Task <GenerationReturn> SaveLinkToPinboard(LinkContent toSave, IProgress <string> progress)
        {
            if (string.IsNullOrWhiteSpace(UserSettingsSingleton.CurrentSettings().PinboardApiToken))
            {
                return(await GenerationReturn.Success("No PinboardApiToken - skipping save to Pinboard",
                                                      toSave.ContentId));
            }

            var descriptionFragments = new List <string>();

            if (!string.IsNullOrWhiteSpace(toSave.Site))
            {
                descriptionFragments.Add($"Site: {toSave.Site}");
            }
            if (toSave.LinkDate != null)
            {
                descriptionFragments.Add($"Date: {toSave.LinkDate.Value:g}");
            }
            if (!string.IsNullOrWhiteSpace(toSave.Description))
            {
                descriptionFragments.Add($"Description: {toSave.Description}");
            }
            if (!string.IsNullOrWhiteSpace(toSave.Comments))
            {
                descriptionFragments.Add($"Comments: {toSave.Comments}");
            }
            if (!string.IsNullOrWhiteSpace(toSave.Author))
            {
                descriptionFragments.Add($"Author: {toSave.Author}");
            }

            var tagList = Db.TagListParse(toSave.Tags);

            tagList.Add(UserSettingsSingleton.CurrentSettings().SiteName);
            tagList = tagList.Select(x => x.Replace(" ", "-")).ToList();

            progress?.Report("Setting up Pinboard");

            var bookmark = new Bookmark
            {
                Url         = toSave.Url,
                Description = toSave.Title,
                Extended    = string.Join(" ;; ", descriptionFragments),
                Tags        = tagList,
                CreatedDate = DateTime.Now,
                Shared      = true,
                ToRead      = false,
                Replace     = true
            };

            try
            {
                using var pb = new PinboardAPI(UserSettingsSingleton.CurrentSettings().PinboardApiToken);
                progress?.Report("Adding Pinboard Bookmark");
                await pb.Posts.Add(bookmark);
            }
            catch (Exception e)
            {
                return(await GenerationReturn.Error("Trouble Saving to Pinboard", toSave.ContentId, e));
            }

            progress?.Report("Pinboard Bookmark Complete");

            return(await GenerationReturn.Success("Saved to Pinboard", toSave.ContentId));
        }
Пример #19
0
        public static async Task <GenerationReturn> CheckForBadContentReferences(IContentCommon content,
                                                                                 PointlessWaymarksContext db, IProgress <string> progress)
        {
            progress?.Report($"Checking ContentIds for {content.Title}");

            var extracted = new List <Guid>();

            if (content.MainPicture != null && content.MainPicture != content.ContentId)
            {
                extracted.Add(content.MainPicture.Value);
            }

            var toSearch = string.Empty;

            toSearch += content.BodyContent + content.Summary;

            if (content is IUpdateNotes updateContent)
            {
                toSearch += updateContent.UpdateNotes;
            }

            if (content is PointContentDto pointDto)
            {
                pointDto.PointDetails.ForEach(x => toSearch += x.StructuredDataAsJson);
            }

            if (content is PointContent point)
            {
                (await Db.PointDetailsForPoint(point.ContentId, db)).ForEach(x => toSearch += x.StructuredDataAsJson);
            }

            if (string.IsNullOrWhiteSpace(toSearch) && !extracted.Any())
            {
                return(await GenerationReturn.Success(
                           $"{Db.ContentTypeString(content)} {content.Title} - No Content Ids Found", content.ContentId));
            }

            extracted.AddRange(BracketCodeCommon.BracketCodeContentIds(toSearch));

            if (!extracted.Any())
            {
                return(await GenerationReturn.Success(
                           $"{Db.ContentTypeString(content)} {content.Title} - No Content Ids Found", content.ContentId));
            }

            progress?.Report($"Found {extracted.Count} ContentIds to check for {content.Title}");

            var notFoundList = new List <Guid>();

            foreach (var loopExtracted in extracted)
            {
                var contentLookup = await db.ContentFromContentId(loopExtracted);

                if (contentLookup != null)
                {
                    continue;
                }
                if (await db.MapComponents.AnyAsync(x => x.ContentId == loopExtracted))
                {
                    continue;
                }

                progress?.Report($"ContentId {loopExtracted} Not Found in Db...");
                notFoundList.Add(loopExtracted);
            }

            if (notFoundList.Any())
            {
                return(await GenerationReturn.Error(
                           $"{Db.ContentTypeString(content)} {content.Title} has " +
                           $"Invalid ContentIds in Bracket Codes - {string.Join(", ", notFoundList)}", content.ContentId));
            }

            return(await GenerationReturn.Success(
                       $"{Db.ContentTypeString(content)} {content.Title} - No Invalid Content Ids Found"));
        }
Пример #20
0
        public static async Task <(GenerationReturn generationReturn, PhotoMetadata metadata)> PhotoMetadataFromFile(
            FileInfo selectedFile, IProgress <string> progress)
        {
            progress?.Report("Starting Metadata Processing");

            selectedFile.Refresh();

            if (!selectedFile.Exists)
            {
                return(await GenerationReturn.Error("File Does Not Exist?"), null);
            }

            var toReturn = new PhotoMetadata();

            progress?.Report("Getting Directories");

            var exifSubIfDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName)
                                     .OfType <ExifSubIfdDirectory>().FirstOrDefault();
            var exifDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <ExifIfd0Directory>()
                                .FirstOrDefault();
            var iptcDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <IptcDirectory>()
                                .FirstOrDefault();
            var gpsDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <GpsDirectory>()
                               .FirstOrDefault();
            var xmpDirectory = ImageMetadataReader.ReadMetadata(selectedFile.FullName).OfType <XmpDirectory>()
                               .FirstOrDefault();

            toReturn.PhotoCreatedBy = exifDirectory?.GetDescription(ExifDirectoryBase.TagArtist) ?? string.Empty;

            if (string.IsNullOrWhiteSpace(toReturn.PhotoCreatedBy))
            {
                toReturn.PhotoCreatedBy = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "creator", 1)?.Value ??
                                          string.Empty;
            }

            if (string.IsNullOrWhiteSpace(toReturn.PhotoCreatedBy))
            {
                toReturn.PhotoCreatedBy = iptcDirectory?.GetDescription(IptcDirectory.TagByLine) ?? string.Empty;
            }

            var createdOn = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagDateTimeOriginal);

            if (string.IsNullOrWhiteSpace(createdOn))
            {
                var gpsDateTime = DateTime.MinValue;
                if (gpsDirectory?.TryGetGpsDate(out gpsDateTime) ?? false)
                {
                    if (gpsDateTime != DateTime.MinValue)
                    {
                        toReturn.PhotoCreatedOn = gpsDateTime.ToLocalTime();
                    }
                }
                else
                {
                    toReturn.PhotoCreatedOn = DateTime.Now;
                }
            }
            else
            {
                var createdOnParsed = DateTime.TryParseExact(
                    exifSubIfDirectory.GetDescription(ExifDirectoryBase.TagDateTimeOriginal), "yyyy:MM:dd HH:mm:ss",
                    CultureInfo.InvariantCulture, DateTimeStyles.None, out var parsedDate);

                toReturn.PhotoCreatedOn = createdOnParsed ? parsedDate : DateTime.Now;
            }

            var isoString = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagIsoEquivalent);

            if (!string.IsNullOrWhiteSpace(isoString))
            {
                toReturn.Iso = int.Parse(isoString);
            }

            toReturn.CameraMake  = exifDirectory?.GetDescription(ExifDirectoryBase.TagMake) ?? string.Empty;
            toReturn.CameraModel = exifDirectory?.GetDescription(ExifDirectoryBase.TagModel) ?? string.Empty;
            toReturn.FocalLength = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagFocalLength) ?? string.Empty;

            toReturn.Lens = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagLensModel) ?? string.Empty;

            if (toReturn.Lens == string.Empty || toReturn.Lens == "----")
            {
                toReturn.Lens = xmpDirectory?.XmpMeta?.GetProperty(XmpConstants.NsExifAux, "Lens")?.Value ??
                                string.Empty;
            }
            if (toReturn.Lens == string.Empty || toReturn.Lens == "----")
            {
                toReturn.Lens =
                    xmpDirectory?.XmpMeta?.GetProperty(XmpConstants.NsCameraraw, "LensProfileName")?.Value ??
                    string.Empty;

                if (toReturn.Lens.StartsWith("Adobe ("))
                {
                    toReturn.Lens = toReturn.Lens.Substring(7, toReturn.Lens.Length - 7);
                    if (toReturn.Lens.EndsWith(")"))
                    {
                        toReturn.Lens = toReturn.Lens.Substring(0, toReturn.Lens.Length - 1);
                    }
                }
            }

            if (toReturn.Lens == "----")
            {
                toReturn.Lens = string.Empty;
            }

            toReturn.Aperture = exifSubIfDirectory?.GetDescription(ExifDirectoryBase.TagAperture) ?? string.Empty;

            toReturn.License = exifDirectory?.GetDescription(ExifDirectoryBase.TagCopyright) ?? string.Empty;

            if (string.IsNullOrWhiteSpace(toReturn.License))
            {
                toReturn.License = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "rights", 1)?.Value ??
                                   string.Empty;
            }

            if (string.IsNullOrWhiteSpace(toReturn.License))
            {
                toReturn.License = iptcDirectory?.GetDescription(IptcDirectory.TagCopyrightNotice) ?? string.Empty;
            }

            var shutterValue = new Rational();

            if (exifSubIfDirectory?.TryGetRational(37377, out shutterValue) ?? false)
            {
                toReturn.ShutterSpeed = ExifHelpers.ShutterSpeedToHumanReadableString(shutterValue);
            }
            else
            {
                toReturn.ShutterSpeed = string.Empty;
            }

            //The XMP data - vs the IPTC - will hold the full Title for a very long title (the IPTC will be truncated) -
            //for a 'from Lightroom with no other concerns' export Title makes the most sense, but there are other possible
            //metadata fields to pull from that could be relevant in other contexts.
            toReturn.Title = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "title", 1)?.Value;

            if (string.IsNullOrWhiteSpace(toReturn.Title))
            {
                toReturn.Title = iptcDirectory?.GetDescription(IptcDirectory.TagObjectName) ?? string.Empty;
            }
            //Use a variety of guess on common file names and make that the title - while this could result in an initial title
            //like DSC001 style out of camera names but after having experimented with loading files I think 'default' is better
            //than an invalid blank.
            if (string.IsNullOrWhiteSpace(toReturn.Title))
            {
                toReturn.Title = Path.GetFileNameWithoutExtension(selectedFile.Name).Replace("-", " ").Replace("_", " ")
                                 .SplitCamelCase();
            }

            toReturn.Summary = iptcDirectory?.GetDescription(IptcDirectory.TagObjectName) ?? string.Empty;

            //2020/3/22 - Process out a convention that I have used for more than a decade of pre-2020s do yyMM at the start of a photo title or in the
            //2020s yyyy MM at the start - hopefully this is matched specifically enough not to accidentally trigger on photos without this info... The
            //base case of not matching this convention is that the year and month from photo created on are added to the front of the title or if the
            //title is blank the photo created on is put as a timestamp into the title. One of the ideas here is to give photos as much of a chance
            //as possible to have valid data for an automated import even when a detail like timestamp title is probably better replaced with something
            //more descriptive.
            if (!string.IsNullOrWhiteSpace(toReturn.Title) && toReturn.Title.StartsWith("2"))
            {
                var possibleTitleDate =
                    Regex.Match(toReturn.Title, @"\A(?<possibleDate>\d\d\d\d[\s-]\d\d[\s-]*).*",
                                RegexOptions.IgnoreCase).Groups["possibleDate"].Value;
                if (!string.IsNullOrWhiteSpace(possibleTitleDate))
                {
                    try
                    {
                        var tempDate = new DateTime(int.Parse(possibleTitleDate.Substring(0, 4)),
                                                    int.Parse(possibleTitleDate.Substring(5, 2)), 1);

                        toReturn.Summary =
                            $"{toReturn.Title.Substring(possibleTitleDate.Length, toReturn.Title.Length - possibleTitleDate.Length)}";
                        toReturn.Title =
                            $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(possibleTitleDate.Length, toReturn.Title.Length - possibleTitleDate.Length)}";

                        progress?.Report("Title updated based on 2yyy MM start pattern for file name");
                    }
                    catch
                    {
                        progress?.Report("Did not successfully parse 2yyy MM start pattern for file name");
                    }
                }
            }
            else if (!string.IsNullOrWhiteSpace(toReturn.Title) &&
                     (toReturn.Title.StartsWith("0") || toReturn.Title.StartsWith("1")))
            {
                try
                {
                    if (Regex.IsMatch(toReturn.Title, @"\A[01]\d\d\d\s.*", RegexOptions.IgnoreCase))
                    {
                        var year  = int.Parse(toReturn.Title.Substring(0, 2));
                        var month = int.Parse(toReturn.Title.Substring(2, 2));

                        var tempDate = year < 20
                            ? new DateTime(2000 + year, month, 1)
                            : new DateTime(1900 + year, month, 1);

                        toReturn.Summary = $"{toReturn.Title.Substring(5, toReturn.Title.Length - 5)}";
                        toReturn.Title   =
                            $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(5, toReturn.Title.Length - 5)}";

                        progress?.Report("Title updated based on YYMM start pattern for file name");
                    }
                }
                catch
                {
                    progress?.Report("Did not successfully parse YYMM start pattern for file name");
                }
            }
            else if (Regex.IsMatch(toReturn.Title, @".*[\s-][01]\d\d\d\z", RegexOptions.IgnoreCase))
            {
                try
                {
                    var year  = int.Parse(toReturn.Title.Substring(toReturn.Title.Length - 4, 2));
                    var month = int.Parse(toReturn.Title.Substring(toReturn.Title.Length - 2, 2));

                    var tempDate = year < 20
                        ? new DateTime(2000 + year, month, 1)
                        : new DateTime(1900 + year, month, 1);

                    toReturn.Summary = $"{toReturn.Title.Substring(0, toReturn.Title.Length - 5)}";
                    toReturn.Title   =
                        $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(0, toReturn.Title.Length - 5)}";

                    progress?.Report("Title updated based on YYMM end pattern for file name");
                }
                catch
                {
                    progress?.Report("Did not successfully parse YYMM end pattern for file name");
                }
            }
            else if (Regex.IsMatch(toReturn.Title, @".*[\s-]\d\d\d\d[\s-]\d\d\z", RegexOptions.IgnoreCase))
            {
                var possibleTitleDate =
                    Regex.Match(toReturn.Title, @".*[\s-](?<possibleDate>\d\d\d\d[\s-]\d\d)\z", RegexOptions.IgnoreCase)
                    .Groups["possibleDate"].Value;
                if (!string.IsNullOrWhiteSpace(possibleTitleDate))
                {
                    try
                    {
                        var tempDate = new DateTime(int.Parse(possibleTitleDate.Substring(0, 4)),
                                                    int.Parse(possibleTitleDate.Substring(5, 2)), 1);

                        toReturn.Summary =
                            $"{toReturn.Title.Substring(0, toReturn.Title.Length - possibleTitleDate.Length)}";
                        toReturn.Title =
                            $"{tempDate:yyyy} {tempDate:MMMM} {toReturn.Title.Substring(0, toReturn.Title.Length - possibleTitleDate.Length)}";

                        progress?.Report("Title updated based on 2yyy MM end pattern for file name");
                    }
                    catch
                    {
                        progress?.Report("Did not successfully parse 2yyy MM end pattern for file name");
                    }
                }
            }
            else
            {
                toReturn.Title = string.IsNullOrWhiteSpace(toReturn.Title)
                    ? toReturn.PhotoCreatedOn.ToString("yyyy MMMM dd h-mm-ss tt")
                    : $"{toReturn.PhotoCreatedOn:yyyy} {toReturn.PhotoCreatedOn:MMMM} {toReturn.Title}";
            }

            //Order is important here - the title supplies the summary in the code above - but overwrite that if there is a
            //description.
            var description = exifDirectory?.GetDescription(ExifDirectoryBase.TagImageDescription) ?? string.Empty;

            if (!string.IsNullOrWhiteSpace(description))
            {
                toReturn.Summary = description;
            }

            //Add a trailing . to the summary if it doesn't end with ! ? .
            if (!toReturn.Summary.EndsWith(".") && !toReturn.Summary.EndsWith("!") && !toReturn.Summary.EndsWith("?"))
            {
                toReturn.Summary = $"{toReturn.Summary}.";
            }

            //Remove multi space from title and summary
            if (!string.IsNullOrWhiteSpace(toReturn.Title))
            {
                toReturn.Title = Regex.Replace(toReturn.Title, @"\s+", " ").TrimNullToEmpty();
            }

            if (!string.IsNullOrWhiteSpace(toReturn.Summary))
            {
                toReturn.Summary = Regex.Replace(toReturn.Summary, @"\s+", " ").TrimNullToEmpty();
            }

            var xmpSubjectKeywordList = new List <string>();

            var xmpSubjectArrayItemCount = xmpDirectory?.XmpMeta?.CountArrayItems(XmpConstants.NsDC, "subject");

            if (xmpSubjectArrayItemCount != null)
            {
                for (var i = 1; i <= xmpSubjectArrayItemCount; i++)
                {
                    var subjectArrayItem = xmpDirectory?.XmpMeta?.GetArrayItem(XmpConstants.NsDC, "subject", i);
                    if (subjectArrayItem == null || string.IsNullOrWhiteSpace(subjectArrayItem.Value))
                    {
                        continue;
                    }
                    xmpSubjectKeywordList.AddRange(subjectArrayItem.Value.Replace(";", ",").Split(",")
                                                   .Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()));
                }
            }

            xmpSubjectKeywordList = xmpSubjectKeywordList.Distinct().ToList();

            var keywordTagList = new List <string>();

            var keywordValue = iptcDirectory?.GetDescription(IptcDirectory.TagKeywords)?.Replace(";", ",") ??
                               string.Empty;

            if (!string.IsNullOrWhiteSpace(keywordValue))
            {
                keywordTagList.AddRange(keywordValue.Replace(";", ",").Split(",")
                                        .Where(x => !string.IsNullOrWhiteSpace(x)).Select(x => x.Trim()));
            }

            if (xmpSubjectKeywordList.Count == 0 && keywordTagList.Count == 0)
            {
                toReturn.Tags = string.Empty;
            }
            else if (xmpSubjectKeywordList.Count >= keywordTagList.Count)
            {
                toReturn.Tags = string.Join(",", xmpSubjectKeywordList);
            }
            else
            {
                toReturn.Tags = string.Join(",", keywordTagList);
            }

            if (!string.IsNullOrWhiteSpace(toReturn.Tags))
            {
                toReturn.Tags = Db.TagListJoin(Db.TagListParse(toReturn.Tags));
            }

            return(await GenerationReturn.Success($"Parsed Photo Metadata for {selectedFile.FullName} without error"),
                   toReturn);
        }
Пример #21
0
        public static async Task <GenerationReturn> Validate(PointContentDto pointContent)
        {
            var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory();

            if (!rootDirectoryCheck.Item1)
            {
                return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}",
                                                    pointContent.ContentId));
            }

            var commonContentCheck = await CommonContentValidation.ValidateContentCommon(pointContent);

            if (!commonContentCheck.valid)
            {
                return(await GenerationReturn.Error(commonContentCheck.explanation, pointContent.ContentId));
            }

            var latitudeCheck = CommonContentValidation.LatitudeValidation(pointContent.Latitude);

            if (!latitudeCheck.isValid)
            {
                return(await GenerationReturn.Error(latitudeCheck.explanation, pointContent.ContentId));
            }

            var longitudeCheck = CommonContentValidation.LongitudeValidation(pointContent.Longitude);

            if (!longitudeCheck.isValid)
            {
                return(await GenerationReturn.Error(longitudeCheck.explanation, pointContent.ContentId));
            }

            var elevationCheck = CommonContentValidation.ElevationValidation(pointContent.Elevation);

            if (!elevationCheck.isValid)
            {
                return(await GenerationReturn.Error(elevationCheck.explanation, pointContent.ContentId));
            }

            var updateFormatCheck = CommonContentValidation.ValidateUpdateContentFormat(pointContent.UpdateNotesFormat);

            if (!updateFormatCheck.isValid)
            {
                return(await GenerationReturn.Error(updateFormatCheck.explanation, pointContent.ContentId));
            }

            foreach (var loopDetails in pointContent.PointDetails)
            {
                if (loopDetails.ContentId == Guid.Empty)
                {
                    return(await GenerationReturn.Error("Point Detail Data must have a valid Content Id",
                                                        loopDetails.ContentId));
                }
                if (loopDetails.PointContentId != pointContent.ContentId)
                {
                    return(await GenerationReturn.Error(
                               $"{loopDetails.DataType} Point Detail isn't assigned to the current point?",
                               loopDetails.ContentId));
                }
                if (string.IsNullOrWhiteSpace(loopDetails.DataType))
                {
                    return(await GenerationReturn.Error("Point Detail Data Type doesn't have a value",
                                                        loopDetails.ContentId));
                }
                if (string.IsNullOrWhiteSpace(loopDetails.StructuredDataAsJson))
                {
                    return(await GenerationReturn.Error($"{loopDetails.DataType} Point Detail doesn't have any data?",
                                                        loopDetails.ContentId));
                }
                try
                {
                    var content =
                        Db.PointDetailDataFromIdentifierAndJson(loopDetails.DataType, loopDetails.StructuredDataAsJson);
                    var contentValidation = content.Validate();

                    if (!contentValidation.isValid)
                    {
                        return(await GenerationReturn.Error(
                                   $"{loopDetails.DataType} Point Detail: {contentValidation.validationMessage}",
                                   pointContent.ContentId));
                    }
                }
                catch (Exception e)
                {
                    return(await GenerationReturn.Error(
                               $"Exception loading the Structured Data for {loopDetails.DataType} Point Detail {e.Message}",
                               pointContent.ContentId));
                }
            }

            return(await GenerationReturn.Success("Point Content Validation Successful"));
        }
Пример #22
0
        public static async Task <GenerationReturn> Validate(LineContent lineContent)
        {
            var rootDirectoryCheck = UserSettingsUtilities.ValidateLocalSiteRootDirectory();

            if (!rootDirectoryCheck.Item1)
            {
                return(await GenerationReturn.Error($"Problem with Root Directory: {rootDirectoryCheck.Item2}",
                                                    lineContent.ContentId));
            }

            var commonContentCheck = await CommonContentValidation.ValidateContentCommon(lineContent);

            if (!commonContentCheck.valid)
            {
                return(await GenerationReturn.Error(commonContentCheck.explanation, lineContent.ContentId));
            }

            var updateFormatCheck = CommonContentValidation.ValidateUpdateContentFormat(lineContent.UpdateNotesFormat);

            if (!updateFormatCheck.isValid)
            {
                return(await GenerationReturn.Error(updateFormatCheck.explanation, lineContent.ContentId));
            }

            try
            {
                var serializer = GeoJsonSerializer.Create(SpatialHelpers.Wgs84GeometryFactory(), 3);

                using var stringReader = new StringReader(lineContent.Line);
                using var jsonReader   = new JsonTextReader(stringReader);
                var featureCollection = serializer.Deserialize <FeatureCollection>(jsonReader);
                if (featureCollection.Count < 1)
                {
                    return(await GenerationReturn.Error(
                               "The GeoJson for the line appears to have an empty Feature Collection?", lineContent.ContentId));
                }
                if (featureCollection.Count > 1)
                {
                    return(await GenerationReturn.Error(
                               "The GeoJson for the line appears to contain multiple elements? It should only contain 1 line...",
                               lineContent.ContentId));
                }
                if (featureCollection[0].Geometry is not LineString)
                {
                    return(await GenerationReturn.Error(
                               "The GeoJson for the line has one element but it isn't a LineString?", lineContent.ContentId));
                }
                var lineString = featureCollection[0].Geometry as LineString;
                if (lineString == null || lineString.Count < 1 || lineString.Length == 0)
                {
                    return(await GenerationReturn.Error("The LineString doesn't have any points or is zero length?",
                                                        lineContent.ContentId));
                }
            }
            catch (Exception e)
            {
                return(await GenerationReturn.Error(
                           $"Error parsing the FeatureCollection and/or problems checking the LineString {e.Message}",
                           lineContent.ContentId));
            }

            return(await GenerationReturn.Success("Line Content Validation Successful"));
        }