def get_kc_profile_data(user_id): ''' Retrieve all fields from the user's KC profile and return them in a dictionary ''' try: profile_model = _models.UserProfile.objects.get(user_id=user_id) # Use a dict instead of the object in case we enter the next exception. # The response will return a json. # We want the variable to have the same type in both cases. profile = profile_model.__dict__ except _models.UserProfile.DoesNotExist: try: response = _trigger_kc_profile_creation( User.objects.get(pk=user_id)) profile = response.json() except _KoboCatProfileException: logging.exception('Failed to create KoBoCAT user profile') return {} fields = [ # Use a (kc_name, new_name) tuple to rename a field 'name', 'organization', ('home_page', 'organization_website'), ('description', 'bio'), ('phonenumber', 'phone_number'), 'address', 'city', 'country', 'require_auth', 'twitter', 'metadata', ] result = {} for field in fields: if isinstance(field, tuple): kc_name, field = field else: kc_name = field value = profile.get(kc_name) # When a field contains JSON (e.g. `metadata`), it gets loaded as a # `dict`. Convert it back to a string representation if isinstance(value, dict): value = json.dumps(value) result[field] = value return result
def handle(self, *args, **options): if not settings.KOBOCAT_URL or not settings.KOBOCAT_INTERNAL_URL: raise ImproperlyConfigured( 'Both KOBOCAT_URL and KOBOCAT_INTERNAL_URL must be ' 'configured before using this command') self._quiet = options.get('quiet') users = User.objects.all() # Do a basic query just to make sure the ReadOnlyXForm model is loaded if not ReadOnlyXForm.objects.exists(): return self._print_str('%d total users' % users.count()) # A specific user or everyone? if options.get('username'): users = User.objects.filter(username=options.get('username')) self._print_str('%d users selected' % users.count()) # Only users who prefer KPI or all users? if not options.get('all_users'): users = users.filter( models.Q(formbuilderpreference__preferred_builder= FormBuilderPreference.KPI) | models.Q(formbuilderpreference=None) # KPI is the default now ) self._print_str('%d of selected users prefer KPI' % users.count()) # We'll be copying the date fields from KC, so don't auto-update them _set_auto_field_update(Asset, "date_created", False) _set_auto_field_update(Asset, "date_modified", False) for user in users: # Make sure the user has a token for access to KC's API Token.objects.get_or_create(user=user) existing_surveys = user.assets.filter(asset_type='survey') # Each asset that the user has already deployed to KC should have a # form uuid stored in its deployment data xform_uuids_to_asset_pks = {} for existing_survey in existing_surveys: dd = existing_survey._deployment_data try: backend_response = dd['backend_response'] except KeyError: continue xform_uuids_to_asset_pks[backend_response['uuid']] = \ existing_survey.pk xforms = user.xforms.all() for xform in xforms: try: with transaction.atomic(): if xform.uuid not in xform_uuids_to_asset_pks: # This is an orphaned KC form. Build a new asset to # match asset = Asset(asset_type='survey', owner=user) asset.name = _make_name_for_asset(asset, xform) else: asset = Asset.objects.get( pk=xform_uuids_to_asset_pks[xform.uuid]) changes = [] try: content_changed = _sync_form_content( asset, xform, changes) metadata_changed = _sync_form_metadata( asset, xform, changes) except SyncKCXFormsWarning as e: error_information = [ 'WARN', user.username, xform.id_string, e.message ] self._print_tabular(*error_information) continue if content_changed or metadata_changed: # preserve the original "asset.content" asset.save(adjust_content=False) # save a new version with standardized content asset.save() if content_changed: asset._mark_latest_version_as_deployed() self._print_tabular(','.join(changes), user.username, xform.id_string, asset.uid) else: self._print_tabular('NOOP', user.username, xform.id_string, asset.uid) except Exception as e: error_information = [ 'FAIL', user.username, xform.id_string, repr(e) ] self._print_tabular(*error_information) logging.exception(u'sync_kobocat_xforms: {}'.format( u', '.join(error_information))) _set_auto_field_update(Asset, "date_created", True) _set_auto_field_update(Asset, "date_modified", True)
def handle(self, *args, **options): if not settings.KOBOCAT_URL or not settings.KOBOCAT_INTERNAL_URL: raise ImproperlyConfigured( 'Both KOBOCAT_URL and KOBOCAT_INTERNAL_URL must be ' 'configured before using this command') self._quiet = options.get('quiet') username = options.get('username') populate_xform_kpi_asset_uid = options.get( 'populate_xform_kpi_asset_uid') sync_kobocat_form_media = options.get('sync_kobocat_form_media') verbosity = options.get('verbosity') users = User.objects.all() # Do a basic query just to make sure the KobocatXForm model is # loaded if not KobocatXForm.objects.exists(): return self._print_str('%d total users' % users.count()) # A specific user or everyone? if username: users = User.objects.filter(username=username) self._print_str('%d users selected' % users.count()) # We'll be copying the date fields from KC, so don't auto-update them _set_auto_field_update(Asset, "date_created", False) _set_auto_field_update(Asset, "date_modified", False) for user in users: # Make sure the user has a token for access to KC's API Token.objects.get_or_create(user=user) existing_surveys = user.assets.filter(asset_type='survey') # Each asset that the user has already deployed to KC should have a # form uuid stored in its deployment data xform_uuids_to_asset_pks = {} for existing_survey in existing_surveys: if not existing_survey.has_deployment: continue backend_response = existing_survey.deployment.backend_response xform_uuids_to_asset_pks[backend_response['uuid']] = \ existing_survey.pk # KobocatXForm has a foreign key on KobocatUser, not on User xforms = KobocatXForm.objects.filter(user_id=user.pk).all() for xform in xforms: try: with transaction.atomic(): if xform.uuid not in xform_uuids_to_asset_pks: # This is an orphaned KC form. Build a new asset to # match asset = Asset(asset_type='survey', owner=user) asset.name = _make_name_for_asset(asset, xform) else: asset = Asset.objects.get( pk=xform_uuids_to_asset_pks[xform.uuid]) changes = [] try: content_changed = _sync_form_content( asset, xform, changes) metadata_changed = _sync_form_metadata( asset, xform, changes) except SyncKCXFormsWarning as e: error_information = [ 'WARN', user.username, xform.id_string, e.message ] self._print_tabular(*error_information) continue if content_changed or metadata_changed: # preserve the original "asset.content" asset.save(adjust_content=False) # save a new version with standardized content asset.save() if content_changed: asset._mark_latest_version_as_deployed() self._print_tabular(','.join(changes), user.username, xform.id_string, asset.uid) else: self._print_tabular('NOOP', user.username, xform.id_string, asset.uid) except Exception as e: error_information = [ 'FAIL', user.username, xform.id_string, repr(e) ] self._print_tabular(*error_information) logging.exception('sync_kobocat_xforms: {}'.format( ', '.join(error_information))) _set_auto_field_update(Asset, "date_created", True) _set_auto_field_update(Asset, "date_modified", True) if populate_xform_kpi_asset_uid: call_command( 'populate_kc_xform_kpi_asset_uid', username=username, verbosity=verbosity, ) if sync_kobocat_form_media: call_command( 'sync_kobocat_form_media', username=username, quiet=self._quiet, verbosity=verbosity, )