diff --git a/data/database.py b/data/database.py index acc8f3fe7..45aa0daf7 100644 --- a/data/database.py +++ b/data/database.py @@ -337,7 +337,8 @@ class User(BaseModel): enabled = BooleanField(default=True) invoice_email_address = CharField(null=True, index=True) - name = CharField(null=True) + given_name = CharField(null=True) + family_name = CharField(null=True) company = CharField(null=True) def delete_instance(self, recursive=False, delete_nullable=False): diff --git a/data/migrations/versions/491a530df230_add_user_metadata_fields.py b/data/migrations/versions/faf752bd2e0a_add_user_metadata_fields.py similarity index 73% rename from data/migrations/versions/491a530df230_add_user_metadata_fields.py rename to data/migrations/versions/faf752bd2e0a_add_user_metadata_fields.py index 4f7cc8abd..8822ff06a 100644 --- a/data/migrations/versions/491a530df230_add_user_metadata_fields.py +++ b/data/migrations/versions/faf752bd2e0a_add_user_metadata_fields.py @@ -1,23 +1,25 @@ """Add user metadata fields -Revision ID: 491a530df230 +Revision ID: faf752bd2e0a Revises: 6c7014e84a5e -Create Date: 2016-11-04 18:03:05.237408 +Create Date: 2016-11-14 17:29:03.984665 """ # revision identifiers, used by Alembic. -revision = '491a530df230' +revision = 'faf752bd2e0a' down_revision = '6c7014e84a5e' from alembic import op import sqlalchemy as sa + from util.migrate import UTF8CharField def upgrade(tables): ### commands auto generated by Alembic - please adjust! ### op.add_column('user', sa.Column('company', UTF8CharField(length=255), nullable=True)) - op.add_column('user', sa.Column('name', UTF8CharField(length=255), nullable=True)) + op.add_column('user', sa.Column('family_name', UTF8CharField(length=255), nullable=True)) + op.add_column('user', sa.Column('given_name', UTF8CharField(length=255), nullable=True)) ### end Alembic commands ### op.bulk_insert(tables.userpromptkind, @@ -29,7 +31,8 @@ def upgrade(tables): def downgrade(tables): ### commands auto generated by Alembic - please adjust! ### - op.drop_column('user', 'name') + op.drop_column('user', 'given_name') + op.drop_column('user', 'family_name') op.drop_column('user', 'company') ### end Alembic commands ### diff --git a/data/model/user.py b/data/model/user.py index 86e828a08..24c5b0ede 100644 --- a/data/model/user.py +++ b/data/model/user.py @@ -355,10 +355,11 @@ def list_entity_robot_permission_teams(entity_name, include_permissions=False): return TupleSelector(query, fields) -def update_user_metadata(user, name=None, company=None): +def update_user_metadata(user, given_name=None, family_name=None, company=None): """ Updates the metadata associated with the user, including his/her name and company. """ with db_transaction(): - user.name = name or user.name + user.given_name = given_name or user.given_name + user.family_name = family_name or user.family_name user.company = company or user.company user.save() diff --git a/endpoints/api/user.py b/endpoints/api/user.py index 5b971791e..5457132e8 100644 --- a/endpoints/api/user.py +++ b/endpoints/api/user.py @@ -213,9 +213,13 @@ class User(ApiResource): 'type': ['string', 'null'], 'description': 'Custom email address for receiving invoices', }, - 'name': { + 'given_name': { 'type': 'string', - 'description': 'The optional entered name for the user', + 'description': 'The optional entered given name for the user', + }, + 'family_name': { + 'type': 'string', + 'description': 'The optional entered family name for the user', }, 'company': { 'type': 'string', @@ -332,10 +336,14 @@ class User(ApiResource): code = model.user.create_confirm_email_code(user, new_email=new_email) send_change_email(user.username, user_data['email'], code.code) else: + user_analytics.change_email(user.email, new_email) model.user.update_email(user, new_email, auto_verify=not features.MAILING) - if 'name' in user_data or 'company' in user_data: - model.user.update_user_metadata(user, user_data.get('name'), user_data.get('company')) + if 'given_name' in user_data or 'family_name' in user_data or 'company' in user_data: + model.user.update_user_metadata(user, user_data.get('given_name'), + user_data.get('family_name'), user_data.get('company')) + user_analytics.change_metadata(user.email, user_data.get('given_name'), + user_data.get('family_name'), user_data.get('company')) # Check for username rename. A username can be renamed if the feature is enabled OR the user # currently has a confirm_username prompt. @@ -353,6 +361,7 @@ class User(ApiResource): raise request_error(message='Username is already in use') user = model.user.change_username(user.id, new_username) + user_analytics.change_username(user.email, new_username) elif confirm_username: model.user.remove_user_prompt(user, 'confirm_username') diff --git a/endpoints/common.py b/endpoints/common.py index bbbed4754..19720a858 100644 --- a/endpoints/common.py +++ b/endpoints/common.py @@ -122,7 +122,8 @@ def common_login(db_user, permanent_session=True): session.permanent_session_lifetime = convert_to_timedelta(session_timeout_str) # Inform our user analytics that we have a new "lead" - user_analytics.create_lead(db_user.email, db_user.username) + user_analytics.create_lead(db_user.email, db_user.username, db_user.given_name, + db_user.family_name, db_user.company) return True else: logger.debug('User could not be logged in, inactive?.') diff --git a/static/partials/update-user.html b/static/partials/update-user.html index ccd2c7e94..7e9ff698b 100644 --- a/static/partials/update-user.html +++ b/static/partials/update-user.html @@ -42,19 +42,28 @@
- +
- +
- +
- + + +
+
+ +
+ +
+ +
diff --git a/test/data/test.db b/test/data/test.db index 1fa5fe01d..13703972b 100644 Binary files a/test/data/test.db and b/test/data/test.db differ diff --git a/util/saas/useranalytics.py b/util/saas/useranalytics.py index 2feeae647..541a1a9bc 100644 --- a/util/saas/useranalytics.py +++ b/util/saas/useranalytics.py @@ -27,13 +27,29 @@ class _MarketoAnalyticsClient(object): self._munchkin_private_key = munchkin_private_key self._lead_source = lead_source - def create_lead(self, email, username): + def _get_lead_metadata(self, given_name, family_name, company): + metadata = {} + if given_name: + metadata['firstName'] = given_name + + if family_name: + metadata['lastName'] = family_name + + if company: + metadata['company'] = company + + return metadata + + def create_lead(self, email, username, given_name, family_name, company): lead_data = dict( email=email, Quay_Username__c=username, leadSource='Web - Product Trial', Lead_Source_Detail__c=self._lead_source, ) + + lead_data.update(self._get_lead_metadata(given_name, family_name, company)) + self._marketo.create_update_leads( action='createOrUpdate', leads=[lead_data], @@ -65,16 +81,30 @@ class _MarketoAnalyticsClient(object): lookupField='id', ) - def change_username(self, email, new_username): - found = self._find_leads_by_email(email) + def change_metadata(self, email, given_name, family_name, company): + lead_data = self._get_lead_metadata(given_name, family_name, company) + if not lead_data: + return - # Update using their user id. - updated = [dict(id=lead['id'], Quay_Username__c=new_username) for lead in found] + # Update using their email address. + lead_data['email'] = email self._marketo.create_update_leads( action='updateOnly', - leads=updated, + leads=[lead_data], asyncProcessing=True, - lookupField='id', + lookupField='email', + ) + + def change_username(self, email, new_username): + # Update using their email. + self._marketo.create_update_leads( + action='updateOnly', + leads=[{ + 'email': email, + 'Quay_Username__c': new_username, + }], + asyncProcessing=True, + lookupField='email', ) @AsyncExecutorWrapper.sync