Skip to content

Commit

Permalink
Add an option to remove emoji in titles and paths (#25)
Browse files Browse the repository at this point in the history
* add option and refactor inner functions to pass options object through instead of individual ones to make it easier to extend

* Create replacement function (untested)

* fix scope

* test

* pass the option through and use it in GetUsableCardName()

* create a GetUsableListName() and use it there, cast and reference GetUsableListName() in GetDuplicateNameKey(), add a test to ensure duplicates are incremented when enabled

* remove emoji from the board name

* extract board-name-cleaning function

* trim trailing and preceding whitespace, and multiple spaces within card, list and board titles
  • Loading branch information
GSGBen authored Sep 17, 2022
1 parent 2bd0d83 commit d93ac6f
Show file tree
Hide file tree
Showing 7 changed files with 238 additions and 37 deletions.
112 changes: 84 additions & 28 deletions T2MDCli/Cli.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Linq;
using System.Threading.Tasks;
using System.Text.Json.Serialization;
using System.Text.RegularExpressions;

namespace GoldenSyrupGames.T2MD
{
Expand Down Expand Up @@ -230,8 +231,10 @@ CliOptions options
List<TrelloActionModel> boardComments = await GetBoardCommentsAsync(trelloApiBoard.ID);

// write the json to file (overwrite).

// without this linux will happily write /'s
string usableBoardName = FileSystem.SanitiseForPath(trelloApiBoard.Name);
string usableBoardName = GetUsableBoardName(trelloApiBoard, options);

string boardOutputFilePath = Path.Combine(_outputPath, $"{usableBoardName}.json");
using FileStream fileStream = File.Create(boardOutputFilePath);
using Stream contentStream = await response.Content
Expand Down Expand Up @@ -272,7 +275,7 @@ await boardJsonStreamReader.ReadToEndAsync().ConfigureAwait(false),
// differentiate duplicate list names
Dictionary<ITrelloCommon, string> duplicateSuffixes = GetDuplicateSuffixes(
orderedLists,
options.MaxCardFilenameTitleLength
options
);

// create folders for each list.
Expand Down Expand Up @@ -306,7 +309,7 @@ await boardJsonStreamReader.ReadToEndAsync().ConfigureAwait(false),
// cards will become files and that makes the most sense for the user
Dictionary<ITrelloCommon, string> duplicateCardSuffixes = GetDuplicateSuffixes(
orderedCards,
options.MaxCardFilenameTitleLength
options
);

// process each card
Expand Down Expand Up @@ -361,6 +364,30 @@ await boardJsonStreamReader.ReadToEndAsync().ConfigureAwait(false),
AnsiConsole.MarkupLine($" [green]Finished {trelloApiBoard.Name}[/]");
}

/// <summary>
/// Returns the name of the board with any filesystem-incompatible characters removed.
/// <para />
/// Trims preceding and trailing whitespace to avoid Windows being unable to use the folder
/// (and to neaten things up). <para />
/// Replaces multiple whitespace with a single space (usually from removing emoji). <para />
/// If specified in options, emoji are removed here as well.
/// </summary>
public static string GetUsableBoardName(
TrelloApiBoardModel trelloApiBoard,
CliOptions options
)
{
string usableBoardName = FileSystem.SanitiseForPath(trelloApiBoard.Name);
if (options.RemoveEmoji)
{
usableBoardName = Emoji.ReplaceEmoji(usableBoardName, "");
}
usableBoardName = usableBoardName.Trim();
Regex multipleSpaces = new Regex("\\s+");
usableBoardName = multipleSpaces.Replace(usableBoardName, " ");
return usableBoardName;
}

/// <summary>
/// Returns the commentCard action models for a board. <para />
/// Retrieves them from the API because the json export only contains the last 1000 actions.
Expand Down Expand Up @@ -466,7 +493,7 @@ string duplicateDifferentiator

// create a folder for each list.
// remove special characters
string usableListName = FileSystem.SanitiseForPath(trelloList.Name);
string usableListName = GetUsableListName(trelloList, options);
if (options.NoNumbering && !string.IsNullOrEmpty(duplicateDifferentiator))
{
usableListName += $" {duplicateDifferentiator}";
Expand Down Expand Up @@ -495,6 +522,27 @@ string duplicateDifferentiator
}
}

/// <summary>
/// Returns the name of the list with any filesystem-incompatible characters removed.
/// <para />
/// Trims preceding and trailing whitespace to avoid Windows being unable to use the folder
/// (and to neaten things up). <para />
/// Replaces multiple whitespace with a single space (usually from removing emoji). <para />
/// If specified in options, emoji are removed here as well.
/// </summary>
public static string GetUsableListName(TrelloListModel trelloList, CliOptions options)
{
string usableListName = FileSystem.SanitiseForPath(trelloList.Name);
if (options.RemoveEmoji)
{
usableListName = Emoji.ReplaceEmoji(usableListName, "");
}
usableListName = usableListName.Trim();
Regex multipleSpaces = new Regex("\\s+");
usableListName = multipleSpaces.Replace(usableListName, " ");
return usableListName;
}

/// <summary>
/// Creates a markdown file each for the board's description, comments, checklists and list
/// of attachments. <para/>
Expand Down Expand Up @@ -528,10 +576,7 @@ string duplicateDifferentiator
{
// restrict the maximum filename length for all files. Just via the title, not any
// suffix or prefix
string usableCardName = GetUsableCardName(
trelloCard,
options.MaxCardFilenameTitleLength
);
string usableCardName = GetUsableCardName(trelloCard, options);

if (options.NoNumbering && !string.IsNullOrEmpty(duplicateDifferentiator))
{
Expand Down Expand Up @@ -612,21 +657,29 @@ await UpdateAttachmentReferencesAsync(

/// <summary>
/// Returns the name of the card limited to the length specified by the user and with any
/// special characters removed.
/// filesystem-incompatible characters removed. <para />
/// Trims preceding and trailing whitespace to avoid Windows being unable to use the folder
/// (and to neaten things up). <para />
/// Replaces multiple whitespace with a single space (usually from removing emoji). <para />
/// If specified in options, emoji are removed here as well.
/// </summary>
private static string GetUsableCardName(
TrelloCardModel trelloCard,
int maxCardFilenameTitleLength
)
public static string GetUsableCardName(TrelloCardModel trelloCard, CliOptions options)
{
int actualOrRestrictedLength = Math.Min(
trelloCard.Name.Length,
maxCardFilenameTitleLength
options.MaxCardFilenameTitleLength
);
string usableCardName = trelloCard.Name.Substring(0, actualOrRestrictedLength);
// remove special characters
// remove special filesystem characters
usableCardName = FileSystem.SanitiseForPath(usableCardName);

// remove emoji if specified
if (options.RemoveEmoji)
{
usableCardName = Emoji.ReplaceEmoji(usableCardName, "");
}
usableCardName = usableCardName.Trim();
Regex multipleSpaces = new Regex("\\s+");
usableCardName = multipleSpaces.Replace(usableCardName, " ");
return usableCardName;
}

Expand Down Expand Up @@ -1058,7 +1111,7 @@ await File.WriteAllTextAsync(commentsPath, replacedCommentsContents)
/// <returns></returns>
public static Dictionary<ITrelloCommon, string> GetDuplicateSuffixes(
IEnumerable<ITrelloCommon> potentialDuplicates,
int maxCardFilenameTitleLength
CliOptions options
)
{
var output = new Dictionary<ITrelloCommon, string>();
Expand All @@ -1068,7 +1121,7 @@ int maxCardFilenameTitleLength

foreach (ITrelloCommon potentialDuplicate in potentialDuplicates)
{
string name = GetDuplicateNameKey(potentialDuplicate, maxCardFilenameTitleLength);
string name = GetDuplicateNameKey(potentialDuplicate, options);
// increment how many times we've seen this name.
int count;
// if in there, grab the current value, then increment it.
Expand All @@ -1094,7 +1147,7 @@ int maxCardFilenameTitleLength
string oneAsString = 1.ToString();
foreach ((ITrelloCommon entry, string suffix) in output)
{
string name = GetDuplicateNameKey(entry, maxCardFilenameTitleLength);
string name = GetDuplicateNameKey(entry, options);
if (suffix == oneAsString && occurrences[name] == 1)
{
output[entry] = "";
Expand All @@ -1105,14 +1158,12 @@ int maxCardFilenameTitleLength
}

/// <summary>
/// Generates the card/list name key used in GetDuplicateSuffixes() so that card names are
/// only differentiated within each list. <para />
/// For lists (or other non-cards) the list name is returned unchanged. <para />
/// For cards the list ID is appended to the card name.
/// Generates the card/list name key used in GetDuplicateSuffixes(). <para/>
/// Card names are differentiated within each list. <para />
/// </summary>
private static string GetDuplicateNameKey(
ITrelloCommon potentialDuplicate,
int maxCardFilenameTitleLength
CliOptions options
)
{
// notes: `as` returns null if the cast fails, casting (prefixing with `(type)`) throws
Expand All @@ -1127,14 +1178,19 @@ int maxCardFilenameTitleLength
// - compare case insensitive. We want to write the original case to disk (so it's
// not in `GetUsableCardName()`) but still avoid overwriting a different one
// that's already been written on case-insensitive systems
string usableCardName = GetUsableCardName(card, maxCardFilenameTitleLength)
.ToLower();
string usableCardName = GetUsableCardName(card, options).ToLower();
return usableCardName + card.IDList;
}
else

var list = potentialDuplicate as TrelloListModel;
if (list != null)
{
return potentialDuplicate.Name.ToLower();
string usableListName = GetUsableListName(list, options).ToLower();
return usableListName;
}

// else
return potentialDuplicate.Name.ToLower();
}
}
}
12 changes: 11 additions & 1 deletion T2MDCli/CliOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
namespace GoldenSyrupGames.T2MD
{
// commandline arguments
class CliOptions
public class CliOptions
{
// The path under which the backups will be stored and the markdown folder structure
// created. Will be created if it doesn't exist.
Expand Down Expand Up @@ -78,6 +78,16 @@ class CliOptions
)]
public bool NoNumbering { get; set; } = false;

/// <summary>
/// Replace emoji with _.
/// </summary>
[Option(
"remove-emoji",
Default = false,
HelpText = "Dropbox doesn't support emoji in filenames.\n" + "This switch removes them."
)]
public bool RemoveEmoji { get; set; } = false;

public static Task PrintUsage(IEnumerable<Error> errors)
{
Console.WriteLine(
Expand Down
Loading

0 comments on commit d93ac6f

Please sign in to comment.