Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions ImageDescriber.Test/ScanTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,62 @@ public void SanitizeFileNameHandlesSimpleName()
Assert.AreEqual("sunset-over-ocean.webp", result.WeakString);
}

[TestMethod]
public void SanitizeFileNameTakesNameFromInsideCodeFence()
{
FileName result = Scan.SanitizeFileName("```\nsunset-over-lake\n```", ".jpg".As<FileExtension>());

Assert.AreEqual("sunset-over-lake.jpg", result.WeakString);
}

[TestMethod]
public void SanitizeFileNameTakesNameFromInsideCodeFenceWithLanguage()
{
FileName result = Scan.SanitizeFileName("```text\r\nsunset-over-lake\r\n```", ".jpg".As<FileExtension>());

Assert.AreEqual("sunset-over-lake.jpg", result.WeakString);
}

[TestMethod]
public void SanitizeFileNameTakesNameFromInlineCodeFence()
{
FileName result = Scan.SanitizeFileName("```sunset-over-lake```", ".jpg".As<FileExtension>());

Assert.AreEqual("sunset-over-lake.jpg", result.WeakString);
}

[TestMethod]
public void SanitizeFileNameSkipsLeadingBlankLines()
{
FileName result = Scan.SanitizeFileName("\nsunset-over-lake\n", ".jpg".As<FileExtension>());

Assert.AreEqual("sunset-over-lake.jpg", result.WeakString);
}

[TestMethod]
public void SanitizeFileNameKeepsDotFollowedByText()
{
FileName result = Scan.SanitizeFileName("st.-louis-arch-at-dusk", ".jpg".As<FileExtension>());

Assert.AreEqual("st.-louis-arch-at-dusk.jpg", result.WeakString);
}

[TestMethod]
public void SanitizeFileNameKeepsDotInTime()
{
FileName result = Scan.SanitizeFileName("sunset-at-5.30pm", ".jpg".As<FileExtension>());

Assert.AreEqual("sunset-at-5.30pm.jpg", result.WeakString);
}

[TestMethod]
public void SanitizeFileNameStripsUpperCaseImageExtension()
{
FileName result = Scan.SanitizeFileName("photo.JPEG", ".png".As<FileExtension>());

Assert.AreEqual("photo.png", result.WeakString);
}

[TestMethod]
public void HashFilesSkipsFilesThatCannotBeRead()
{
Expand Down
22 changes: 13 additions & 9 deletions ImageDescriber/ImageScanner.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,22 +31,26 @@
}

List<AbsoluteFilePath> imageFiles = [];
foreach (string file in Directory.EnumerateFiles(path.WeakString, "*", SearchOption.AllDirectories))

Check warning on line 34 in ImageDescriber/ImageScanner.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Loops should be simplified using the "Where" LINQ method

Check warning on line 34 in ImageDescriber/ImageScanner.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Loops should be simplified using the "Where" LINQ method

Check warning on line 34 in ImageDescriber/ImageScanner.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Loops should be simplified using the "Where" LINQ method

Check warning on line 34 in ImageDescriber/ImageScanner.cs

View workflow job for this annotation

GitHub Actions / Analyze & Release

Loops should be simplified using the "Where" LINQ method

Check warning on line 34 in ImageDescriber/ImageScanner.cs

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Loops should be simplified using the "Where" LINQ method

See more on https://sonarcloud.io/project/issues?id=ktsu-dev_ImageDescriber&issues=AaDmt4dHAduN7hzMrdlh&open=AaDmt4dHAduN7hzMrdlh&pullRequest=169
{
string ext = Path.GetExtension(file);
if (string.IsNullOrEmpty(ext))
{
continue;
}

// FileExtension compares ordinally, and cameras and Windows tools write .JPG/.PNG,
// so fold to the lower-case form the set holds before looking it up.
if (ImageExtensions.Contains(ext.ToLowerInvariant().As<FileExtension>()))
if (IsImageExtension(Path.GetExtension(file)))
{
imageFiles.Add(file.As<AbsoluteFilePath>());
}
}

return imageFiles;
}

internal static bool IsImageExtension(string extension)
{
if (string.IsNullOrEmpty(extension))
{
return false;
}

// FileExtension compares ordinally, and cameras and Windows tools write .JPG/.PNG,
// so fold to the lower-case form the set holds before looking it up.
return ImageExtensions.Contains(extension.ToLowerInvariant().As<FileExtension>());
}
}
29 changes: 17 additions & 12 deletions ImageDescriber/Verbs/Scan.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,18 +246,17 @@ internal static IReadOnlyList<string> DescribeImages(

internal static FileName SanitizeFileName(string rawSuggestion, FileExtension extension)
{
string name = rawSuggestion.Trim().Trim('"', '\'', '`');

// Take only the first line if the model returned multiple lines
int newlineIndex = name.IndexOf('\n', StringComparison.Ordinal);
if (newlineIndex >= 0)
{
name = name[..newlineIndex].Trim();
}

// Strip any extension the model may have included
string existingExt = System.IO.Path.GetExtension(name);
if (!string.IsNullOrEmpty(existingExt))
// Take the first line with content, so a name wrapped in a code fence or preceded by a
// blank line is kept, and only then trim the quotes and backticks around it
string name = rawSuggestion
.Split('\n')
.Select(line => line.Trim())
.FirstOrDefault(line => line.Length > 0 && !IsCodeFence(line)) ?? string.Empty;
name = name.Trim('"', '\'', '`').Trim();

// Strip an image extension the model may have included. A suggestion normally has no
// extension, so any other dot is part of the name ("st.-louis-arch", "sunset-at-5.30pm")
if (ImageScanner.IsImageExtension(System.IO.Path.GetExtension(name)))
{
name = System.IO.Path.GetFileNameWithoutExtension(name);
}
Expand All @@ -283,4 +282,10 @@ internal static FileName SanitizeFileName(string rawSuggestion, FileExtension ex

return $"{name}{extension}".As<FileName>();
}

// An opening or closing fence ("```" or "```text"), as opposed to a name written inline
// between two fences on one line ("```sunset```")
private static bool IsCodeFence(string line) =>
line.StartsWith("```", StringComparison.Ordinal)
&& (line.Length < 6 || !line.EndsWith("```", StringComparison.Ordinal));
}
Loading