-
Notifications
You must be signed in to change notification settings - Fork 59
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Map gender fields on CSV import #2401
Conversation
@ziggabyte can you have a look at this and provide some feedback to @k-nut? Especially around the handling of |
Hi @k-nut ! :D So glad you have taken on this task, it's been very needed! I've played with the deployed version of the code, and also experimented around a bit in your code to give some feedback. I agree with you that the handling of This means the Does this approach make sense to you? |
Thank you for your thoughts. I pushed a version inspired by your proposal, let me know what you think! |
This PR is still in draft mode, but I think it's ready for review so I'm going to mark it as such. Let me know if that was a mistake and we can change it back. |
Oh yes, I forgot. Thank you! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hi! :D Looking nice with some updates! I have given a bit of a more detailed reading of the code and some feedback, if something does not seem right please write your thoughts and we can discuss it!
I also noticed a little bit of logic is missing, that you need to "hook up to" by including GENDER
as a configurable column in this array in MappingRow.tsx
. This will show a button to open the config if you wanna get back to it after going to different fields, and also have a warning message if the mapping is unfinished:
{!showSelect && ( | ||
<Button onClick={() => setMapping(true)}> | ||
<Msg | ||
id={ | ||
messageIds.configuration.configure.genders | ||
.showGenderSelectButton | ||
} | ||
/> | ||
</Button> | ||
)} | ||
{showSelect && ( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This logic, to have a button in place of the Select, was created in the organization mapping because loading and rendering each Select with organisations took a really really long time. So this button was placed there to only load/render each select "on demand". With genders, since no loading of data is happening and it's a small set of values, i think it would be nice to remove the button and just show the select!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice, I thought this was for a nicer UI. Removed 🧹
// selecting `unknown` is like deselecting | ||
onDeselectGender(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure I agree (and I might very well be wrong!) that it should be the same to deselect as to select to import a value as unknown
(null
).
What I mean is, that when selecting unknown
, I want there to be information added to my object of mapping that says "value x should be mapped to null". But deselecting means removing the information related to "value x" in my object of mapping. This comes with the undesired side effect that if I open the gender mapping, and the first value i want to map i select should map to "unknown" - this is not recognised as me having done any mapping at all. Our validation for the mapping will believe no mapping for gender has happened, and it will be forbidden to move on to the validation step.
What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I might be misunderstanding, so please ignore if does not make sense.
I believe that another aspect of this decision is what happens to people for where there is no mapping in the import, but do have gender data in the database.
Not mapping anything doesn't change them in the database (the data
object will not include gender
), while mapping to "unknown" will overwrite whatever is already in the database (setting data.gender
to null
).
So IMO not mapping, and mapping to unknown is two separate actions where the former is a no-op and the latter is destructive.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the implementation to something that should now work for any of the "regular" genders, a deliberate "we want to set this to null" and a "ignore this value in the import"
<PreviewGrid columnHeader={'Gender'} rowValue={null} unmappedRow={true} /> | ||
); | ||
} | ||
|
||
const value = fields?.[fieldKey]; | ||
if (value !== 'm' && value !== 'f' && value !== 'o' && value !== 'null') { | ||
return ( | ||
<PreviewGrid | ||
columnHeader={messages.configuration.preview.columnHeader.gender()} | ||
emptyLabel={messages.configuration.preview.noValue()} | ||
rowValue={null} | ||
unmappedRow={false} | ||
/> | ||
); | ||
} | ||
return ( | ||
<PreviewGrid | ||
columnHeader={'Gender'} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think columnHeader needs to be localised in all the places :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good catch. Added 👍
// Check if there is already a map for this row value to an org ID | ||
const map = column.mapping.find((map) => map.value == value); | ||
// If no map for that value | ||
if (!map) { | ||
const newMap = { gender, value: value }; | ||
dispatch( | ||
// Add value to mapping for the column | ||
columnUpdate([ | ||
columnIndex, | ||
{ | ||
...column, | ||
mapping: [...column.mapping, newMap], | ||
}, | ||
]) | ||
); | ||
} else { | ||
// If there is already a map, replace it | ||
// Find mappings that are not for this row value | ||
const filteredMapping = column.mapping.filter((m) => m.value != value); | ||
// New orgId for that row value | ||
const updatedMap = { ...map, gender }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some little copy-paste mistakes here in the comments, do you wanna updated them to relate to gender mapping and not org mapping? :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is a perfect example of the reasons why I try to avoid comments. They exist to make the code easier to read, but it's so easy to make mistakes like this where the code actually becomes more confusing because of the comment.
Generally, if I feel like a comment is needed, I will try to rewrite the code (and especially name things) so that the code itself becomes easier to read and the comment isn't needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the code was actually way too complicated for what it was doing. I changed the implementation. Let me know what you think!
genders: { | ||
gender: m('Gender'), | ||
genders: { | ||
f: m('female'), | ||
m: m('male'), | ||
null: m('unspecified'), | ||
o: m('other'), | ||
}, | ||
header: m('Gender'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is both a key called header
, and gender
on the same level translated to the same thing, "Gender". I'm thinking that both could be using the same one - where I prefer header
since it communicates better what the transaltion is needed for?
Also an object strucutre thing - genders.genders
does not feel like the optimal naming, could the inner object maybe be called "genderSelectLabels" or something like that, to more communicate what they are used for?
I also think the genders need to have uppercase in the first letter to look nice in the Select
In other parts of the code we use the word "unknown" instead of "unspecified", I think this could be good for consistency.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call, I've changed them to be more descriptive I hope.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Noticed only a small thing when playing around with it right now - that the values in the select are still all lower case :)
f: m('female'), | ||
m: m('male'), | ||
o: m('other'), | ||
unknown: m('unspecified'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Tiny thing here - still in need of uppercase on the options, and I think "unspecified" should be changed to "unknown" as the value! :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, it did feel like I was forgetting something 🙈
f: m('female'), | ||
m: m('male'), | ||
o: m('other'), | ||
unknown: m('unspecified'), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also changed ✅
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Playing with this feels like it behaves the way i expect it to! I'm very happy that you added some very neat simplifications to the code that I wrote for org mapping, it's super fun to have you on board contributing :D
Giving you the seal of approval: 🦭 !
Description
This PR allows users to properly map the gender fields form their CSV data to the gender definition that Zetkin understands
Screenshots
[Add screenshots here]
Changes
Inspired by the enum and org mappings, this adds config and preview components for the gender field.
Notes to reviewer
The null handling is pretty ugly right now (just replacing
null
with"null"
so that it can also be used in translations). I'm also unsure how it should generally be handled and how much translations should be duplicated. Any input is appreciated.Related issues
Resolves #2348