Skip to content

Accept an iHateMoney project name where the id is expected - #99

Open
Pascal-SAPUI5 wants to merge 1 commit into
InteractionEngineer:mainfrom
Pascal-SAPUI5:fix/ihatemoney-project-id-from-name
Open

Pascal-SAPUI5 wants to merge 1 commit into
InteractionEngineer:mainfrom
Pascal-SAPUI5:fix/ihatemoney-project-id-from-name

Conversation

@Pascal-SAPUI5

Copy link
Copy Markdown

Fixes #53.

Adding an iHateMoney project by hand asks for something its web UI never shows.
iHateMoney addresses projects by an id derived from the name, so a project
called "Spongebob house" lives at spongebob-house — but the name is the only
string a user ever sees. Entering it produced a project that was added, stayed
blank, and gave no hint why. Four people reported this on #53 between 2022 and
2023.

This picks up the offer made there:

I'm happy to accept a pull request which automatically converts spaces to
dashes and lowercases the words.

What it does

The add-project form now converts what was typed the same way the server does,
mirroring slugify() in ihatemoney/utils.py:

  1. NFKD-normalise, so accents can be separated from their letters
  2. drop everything that is not a word character, whitespace or a hyphen —
    this is what removes the accents, and any punctuation
  3. trim, lowercase
  4. collapse runs of hyphens and whitespace into a single hyphen

That also explains the second half of the thread: something + somethingelse
becomes something-somethingelse, not something-+-somethingelse, because the
+ is dropped in step 2 before the gap collapses in step 4.

The conversion is idempotent, so anyone who does enter the real id is
unaffected.

Cospend is addressed by a case-sensitive share token and is deliberately left
untouched — normalising that would break every Cospend project. The choice sits
in ProjectBackend.projectIdentifier(fromUserInput:) so it is explicit and
testable rather than buried in the Combine chain.

Changes

  • PayForMe/Util/Util.swiftString.iHateMoneyProjectId
  • PayForMe/Model/Project.swiftProjectBackend.projectIdentifier(fromUserInput:)
  • PayForMe/Views/Projects/Manual/AddProjectManualViewModel.swift — use it when
    building the project from the form
  • PayForMeTests/ProjectIdentifierTests.swift — new

Verifying

Ten tests, including both cases named in the issue:

  • testASpaceBecomesAHyphen — "Spongebob house" → spongebob-house
  • testPunctuationIsDroppedAndTheGapCollapses — "something + somethingelse" →
    something-somethingelse
  • testAnIdThatIsAlreadyCorrectIsUnchanged — idempotence
  • testCospendInputIsUntouched — a mixed-case Cospend token survives verbatim
  • plus accents, repeated separators, surrounding whitespace, underscores, and
    a string with nothing usable in it

By hand: add an iHateMoney project by typing the project's name rather than its
id. It now loads.

Not covered

The thread also mentions "Could not find server" being shown when the project,
not the server, is what could not be found. That is a separate problem in how
the error is reported and is not touched here.

Adding an iHateMoney project by hand asks for something the web UI never
shows. iHateMoney addresses projects by an id it derives from the name, so a
project called "Spongebob house" lives at "spongebob-house" — but the name is
the only string a user ever sees. Entering it produced a project that was
added, stayed blank, and gave no hint why.

The add-project form now converts what was typed the same way the server does:
NFKD-normalise, drop everything that is not a word character, whitespace or a
hyphen, lowercase, then collapse runs of hyphens and whitespace into one. That
mirrors `slugify()` in ihatemoney/utils.py, and it is idempotent — anyone who
does enter the real id is unaffected.

Cospend is addressed by a case-sensitive share token and is deliberately left
alone; normalising that would break every Cospend project.

The conversion also explains the second half of InteractionEngineer#53: "something + somethingelse"
becomes "something-somethingelse", not "something-+-somethingelse", because the
punctuation is dropped before the gap collapses.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

@InteractionEngineer InteractionEngineer left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi Pascal,

I've looked into you code and if you / your agent changes the following aspects, I'd like to merge your PR:

  • Overuse of comments: The three doc comments on iHateMoneyProjectId, projectIdentifier(fromUserInput:) and the call site in AddProjectManualViewModel all explain the same thing. Please keep the explanation once, on iHateMoneyProjectId, and drop the other two.
  • testAStringWithNothingUsableBecomesEmpty documents that the conversion can return "", but the guard in validatedInput still checks the raw input, not the result. An input like !?! therefore builds a project with an empty id and fires a request against the collection endpoint. Please guard on the converted identifier instead.
  • The form still reads "Project ID & Password" / "Enter project id" for both backends. Since iHateMoney now accepts the name, the iHateMoney variant of that label should say so — otherwise #53's actual complaint (being asked for an id that the web UI never shows) is still on screen.
  • The conversion is only tested in isolation. AddProjectManuallyTests already has the pattern (testProjectCreation_tokenBasedCospend asserts on the Project emitted by validatedInput) — please add an iHateMoney counterpart that checks projectId and token. Both must carry the converted value: projectId builds the URL path, token is the Basic-Auth username. Converting only one fails auth while the URL looks right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Blank project when adding iHateMoney with differing project name and ID

2 participants