def setUp(self, test_patch): self.global_config = globalSetup() self.scag = Region.objects.create( name='SCAG', key='scag_dm', scope='scag_dm', bounds= 'MULTIPOLYGON (((-20037508.3399999998509884 -20037508.3399999998509884, -20037508.3399999998509884 20037508.3399999998509884, \ 20037508.3399999998509884 20037508.3399999998509884, 20037508.3399999998509884 -20037508.3399999998509884, \ -20037508.3399999998509884 -20037508.3399999998509884)))', parent_config_entity=self.global_config) update_or_create_group('admin', config_entity=self.scag, superiors=['superadmin']) update_or_create_user(username='******', password='******', email='*****@*****.**', api_key=None, groups=['admin']) self.test_admin = User.objects.get(username='******') self.client = Client()
def test_update_user(self): self.client.get('/footprint/api/v1/user/?format=json&[email protected]&password=test_superadmin_user@uf') update_or_create_group('admin', superiors=['superadmin']) update_or_create_group('manager', superiors=['admin']) update_or_create_group('user', superiors=['admin']) update_or_create_group('scag__or_cnty__admin', self.orange, superiors=['superadmin']) update_or_create_user(username='******', password='******', email='*****@*****.**', first_name='Test', last_name='User', api_key=None, groups=['scag__or_cnty__admin'], is_super_user=False) user_to_update = User.objects.get(username='******') resp = self.client.post( '/footprint/user/{}/'.format(user_to_update.id), dict( username='******', raw_password='******', confirm_password='******', email='*****@*****.**', role='admin', config_entity=self.orange.id, first_name='Changed' ), follow=True ) self.assertEqual(200, resp.status_code) match = re.search(r'User successfully updated', resp.content) self.assertTrue(bool(match)) self.assertEqual('Changed', User.objects.get(id=user_to_update.id).first_name)
def globalSetup(): # Bootstrap GlobalConfig._no_post_save_publishing = True GlobalConfig.objects.update_or_create( key=Keys.GLOBAL_CONFIG_KEY, defaults=dict(bounds=GEOSGeometry('MULTIPOLYGON EMPTY'))) GlobalConfig._no_post_save_publishing = False update_or_create_group('superadmin') update_or_create_user(username='******', password='******', email='*****@*****.**', api_key=None, groups=['superadmin'], is_super_user=True) return GlobalConfig.objects.update_or_create( key='global', defaults=dict( name='Global Config', scope='global', bounds= 'MULTIPOLYGON (((-20037508.3399999998509884 -20037508.3399999998509884, -20037508.3399999998509884 20037508.3399999998509884, \ 20037508.3399999998509884 20037508.3399999998509884, 20037508.3399999998509884 -20037508.3399999998509884, \ -20037508.3399999998509884 -20037508.3399999998509884)))'))[0]
def user(self, username, password, email, group, api_key=None): """ Create a user. :return: """ update_or_create_user(username, email, password, api_key=None, groups=[group])
def application_initialization(**kwargs): """ Initialize or sync the application :param kwargs: 'limit_to_classes' as an array of ConfigEntity classes to limit processing to those 'no_post_save_publishing' set True to prevent the GlobalConfig save from starting publishers """ # Initialize lookup table data if SouthMigrationHistory.objects.filter(app_name='main').exists(): initialize_table_definitions() initialize_client_data() # Bootstrap the GlobalConfig. We'll fill it out later GlobalConfig._no_post_save_publishing = True global_config = GlobalConfig.objects.update_or_create( key=Keys.GLOBAL_CONFIG_KEY, defaults=dict(bounds=GEOSGeometry('MULTIPOLYGON EMPTY')))[0] GlobalConfig._no_post_save_publishing = False # Bootstrap the admin group and user so we can use them beforehand update_or_create_group(name=UserGroupKey.SUPERADMIN, config_entity=global_config) # These users have the same name as their group update_or_create_user(username=UserGroupKey.SUPERADMIN, password='******', groups=[UserGroupKey.SUPERADMIN], is_super_user=True) # Bootstrap the global config Behaviors behavior_fixture = resolve_fixture("behavior", "behavior", BehaviorFixture, 'global', **kwargs) for behavior in behavior_fixture.behaviors(): update_or_create_behavior(behavior) # Boot strap the global config AttributeGroups attribute_group_fixture = resolve_fixture("behavior", "attribute_group", AttributeGroupFixture, 'global', **kwargs) for attribute_group in attribute_group_fixture.attribute_groups(): update_or_create_attribute_group(attribute_group) # Cartocss template storage create_media_subdir('styles') create_media_subdir('cartocss') # Sync the DBEntities to tables in the global schema global_config = initialize_global_config(**kwargs) for region_fixture in region_fixtures(): # Create the Behavior instances. # These can be defined at the Region scope, but most # are simply defined at default_behavior.py behavior_fixture = resolve_fixture("behavior", "behavior", BehaviorFixture, region_fixture.schema, **kwargs) return map(lambda behavior: update_or_create_behavior(behavior), behavior_fixture.behaviors())
def user(request, user_id): user = get_object_or_404(USER_MODEL, id=user_id) if request.user.id != user.id: if get_role_key_for_user(user) not in USER_EDIT_PERMISSIONS_MAP[ get_role_key_for_user(request.user)]: return HttpResponse('Unauthorized', status=401) role_choices, config_entity_choices = get_config_entity_and_role_choices_for_user( request.user) form = UserForm(request.POST or None, instance=user, role_choices=role_choices, initial_role=ROLE_ID_MAP[get_role_key_for_user(user)]) group_formset_class = get_group_formset_class(config_entity_choices) initial_group_values = [{'config_entity': g.id} for g in user.groups.all()] group_formset = group_formset_class(request.POST or None, initial=initial_group_values) if form.is_valid() and group_formset.is_valid(): update_or_create_user( username=user.username, password=form.cleaned_data.get('password') or form.cleaned_data.get('new_password'), email=form.cleaned_data.get('email'), first_name=form.cleaned_data.get('first_name'), last_name=form.cleaned_data.get('last_name'), api_key=None, is_active=str(form.cleaned_data.get('is_active')), groups=get_group_names_from_formset(group_formset)) messages.add_message(request, messages.SUCCESS, 'User successfully updated.') return HttpResponseRedirect('/footprint/users/') if form.errors: for error in form.errors: messages.add_message(request, messages.ERROR, error) if group_formset.non_form_errors(): for error in group_formset.non_form_errors(): messages.add_message(request, messages.ERROR, error) return render( request, 'footprint/user.html', { 'form': form, 'group_formset': group_formset, 'user_id': user.id, 'requesting_user_role': get_role_key_for_user(request.user), 'config_entity_choices': json.dumps(config_entity_choices), 'admin_user': request.user })
def user(request, user_id): user = get_object_or_404(USER_MODEL, id=user_id) if request.user.id != user.id: if get_role_key_for_user(user) not in USER_EDIT_PERMISSIONS_MAP[get_role_key_for_user(request.user)]: return HttpResponse('Unauthorized', status=401) role_choices, config_entity_choices = get_config_entity_and_role_choices_for_user(request.user) form = UserForm( request.POST or None, instance=user, role_choices=role_choices, initial_role=ROLE_ID_MAP[get_role_key_for_user(user)] ) group_formset_class = get_group_formset_class(config_entity_choices) initial_group_values = [{'config_entity': g.id} for g in user.groups.all()] group_formset = group_formset_class(request.POST or None, initial=initial_group_values) if form.is_valid() and group_formset.is_valid(): update_or_create_user( username=user.username, password=form.cleaned_data.get('password') or form.cleaned_data.get('new_password'), email=form.cleaned_data.get('email'), first_name=form.cleaned_data.get('first_name'), last_name=form.cleaned_data.get('last_name'), api_key=None, groups=get_group_names_from_formset(group_formset) ) messages.add_message(request, messages.SUCCESS, 'User successfully updated.') return HttpResponseRedirect('/footprint/users/') if form.errors: for error in form.errors: messages.add_message(request, messages.ERROR, error) if group_formset.non_form_errors(): for error in group_formset.non_form_errors(): messages.add_message(request, messages.ERROR, error) return render( request, 'footprint/user.html', { 'form': form, 'group_formset': group_formset, 'user_id': user.id, 'requesting_user_role': get_role_key_for_user(request.user), 'config_entity_choices': json.dumps(config_entity_choices), 'admin_user': request.user } )
def add_user(request): if get_role_key_for_user(request.user) not in [ UserGroupKey.SUPERADMIN, UserGroupKey.ADMIN, UserGroupKey.MANAGER ]: return HttpResponse('Unauthorized', status=401) role_choices, config_entity_choices = get_config_entity_and_role_choices_for_user( request.user) form = UserForm(request.POST or None, initial={'is_active': True}, role_choices=role_choices) group_formset_class = get_group_formset_class(config_entity_choices) group_formset = group_formset_class(request.POST or None) if form.is_valid() and group_formset.is_valid(): groups = str( get_group_names_from_formset(group_formset)[0]).split('__') seq_id = len( Group.objects.get(name=get_group_names_from_formset(group_formset) [0]).user_set.all()) + 1 new_user = '******'.format(groups[-2], groups[-1], seq_id) update_or_create_user( username=new_user, password=form.cleaned_data.get('password'), email=form.cleaned_data.get('email'), first_name=form.cleaned_data.get('first_name'), last_name=form.cleaned_data.get('last_name'), is_active=str(form.cleaned_data.get('is_active')), api_key=None, groups=get_group_names_from_formset(group_formset)) messages.add_message(request, messages.SUCCESS, 'User successfully added.') return HttpResponseRedirect('/footprint/users/') if form.errors: for error in form.errors: messages.add_message(request, messages.ERROR, error) if group_formset.non_form_errors(): for error in group_formset.non_form_errors(): messages.add_message(request, messages.ERROR, error) return render( request, 'footprint/user.html', { 'form': form, 'group_formset': group_formset, 'requesting_user_role': get_role_key_for_user(request.user), 'config_entity_choices': json.dumps(config_entity_choices), 'admin_user': request.user })
def add_user(request): if get_role_key_for_user(request.user) not in [UserGroupKey.SUPERADMIN, UserGroupKey.ADMIN, UserGroupKey.MANAGER]: return HttpResponse('Unauthorized', status=401) role_choices, config_entity_choices = get_config_entity_and_role_choices_for_user(request.user) form = UserForm( request.POST or None, initial={'is_active': True}, role_choices=role_choices ) group_formset_class = get_group_formset_class(config_entity_choices) group_formset = group_formset_class(request.POST or None) if form.is_valid() and group_formset.is_valid(): update_or_create_user( # TODO: # Unfortunately, it appears that changing the length of the username in Django get complicated # quickly so we're leaving this to 30 characters for now. # See http://stackoverflow.com/questions/2610088/can-djangos-auth-user-username-be-varchar75-how-could-that-be-done username=form.cleaned_data.get('email')[:30], password=form.cleaned_data.get('password'), email=form.cleaned_data.get('email'), first_name=form.cleaned_data.get('first_name'), last_name=form.cleaned_data.get('last_name'), api_key=None, groups=get_group_names_from_formset(group_formset) ) messages.add_message(request, messages.SUCCESS, 'User successfully added.') return HttpResponseRedirect('/footprint/users/') if form.errors: for error in form.errors: messages.add_message(request, messages.ERROR, error) if group_formset.non_form_errors(): for error in group_formset.non_form_errors(): messages.add_message(request, messages.ERROR, error) return render( request, 'footprint/user.html', { 'form': form, 'group_formset': group_formset, 'requesting_user_role': get_role_key_for_user(request.user), 'config_entity_choices': json.dumps(config_entity_choices), 'admin_user': request.user } )
def on_config_entity_post_save_user(sender, **kwargs): config_entity = InstanceBundle.extract_single_instance(**kwargs) if config_entity._no_post_save_publishing: return if kwargs.get('created') and not config_entity.creator: # Set the ConfigEntity.creator to the default admin group user if it wasn't set by the API config_entity.creator = User.objects.get( username=UserGroupKey.SUPERADMIN) config_entity._no_post_save_publishing = True config_entity.save() config_entity._no_post_save_publishing = False # TODO these should be importable on top. Something is messed up user_fixture = resolve_fixture("user", "user", UserFixture, config_entity.schema(), config_entity=config_entity) # Get the ConfigEntityGroups of the ConfigEntity. GlobalConfig uses SuperAdmin as its Group config_entity_groups = config_entity.config_entity_groups() if \ not isinstance(config_entity, GlobalConfig) else \ [Group.objects.get(name=UserGroupKey.SUPERADMIN)] # Find all existing users of all ConfigEntity Groups of the ConfigEntity # Note that we use values() instead of all() to get dicts with just needed fields instead of model instances # TODO remove username from here once all users have emails. update_or_create_user() checks username for uniquess presently existing_user_dicts = flat_map( lambda group: group.user_set.all().values('email', 'username'), config_entity_groups) # Combine the existing users with the fixtures, giving the former preference. We favor # what's in the database because the user might have updated their profile # Only accept fixture users not matching users in the db (by email) existing_emails = map( lambda existing_user_dict: existing_user_dict['email'], existing_user_dicts) logger.debug("Found existing users %s" % ', '.join(existing_emails)) new_fixture_users = filter( lambda fixture_user: fixture_user['email'] not in existing_emails, user_fixture.users()) if len(new_fixture_users) > 0: logger.debug("Found new fixture users %s" % ', '.join( map(lambda fixture_user: fixture_user['email'], new_fixture_users))) user_dicts = existing_user_dicts + new_fixture_users # Update or create each user. This will create users of new fixtures and run post-save processing # on both existing and new. for user_dict in user_dicts: update_or_create_user(**user_dict) reset_queries()
def add_user(request): if get_role_key_for_user(request.user) not in [ UserGroupKey.SUPERADMIN, UserGroupKey.ADMIN, UserGroupKey.MANAGER ]: return HttpResponse('Unauthorized', status=401) role_choices, config_entity_choices = get_config_entity_and_role_choices_for_user( request.user) form = UserForm(request.POST or None, initial={'is_active': True}, role_choices=role_choices) group_formset_class = get_group_formset_class(config_entity_choices) group_formset = group_formset_class(request.POST or None) if form.is_valid() and group_formset.is_valid(): update_or_create_user( # TODO: # Unfortunately, it appears that changing the length of the username in Django get complicated # quickly so we're leaving this to 30 characters for now. # See http://stackoverflow.com/questions/2610088/can-djangos-auth-user-username-be-varchar75-how-could-that-be-done username=form.cleaned_data.get('email')[:30], password=form.cleaned_data.get('password'), email=form.cleaned_data.get('email'), first_name=form.cleaned_data.get('first_name'), last_name=form.cleaned_data.get('last_name'), api_key=None, groups=get_group_names_from_formset(group_formset)) messages.add_message(request, messages.SUCCESS, 'User successfully added.') return HttpResponseRedirect('/footprint/users/') if form.errors: for error in form.errors: messages.add_message(request, messages.ERROR, error) if group_formset.non_form_errors(): for error in group_formset.non_form_errors(): messages.add_message(request, messages.ERROR, error) return render( request, 'footprint/user.html', { 'form': form, 'group_formset': group_formset, 'requesting_user_role': get_role_key_for_user(request.user), 'config_entity_choices': json.dumps(config_entity_choices), 'admin_user': request.user })
def application_initialization(**kwargs): """ Initialize or sync the application :param kwargs: 'limit_to_classes' as an array of ConfigEntity classes to limit processing to those 'no_post_save_publishing' set True to prevent the GlobalConfig save from starting publishers """ # Initialize lookup table data if SouthMigrationHistory.objects.filter(app_name='main').exists(): initialize_table_definitions() initialize_client_data() # Bootstrap the GlobalConfig. We'll fill it out later GlobalConfig._no_post_save_publishing = True global_config = GlobalConfig.objects.update_or_create( key=Keys.GLOBAL_CONFIG_KEY, defaults=dict(bounds=GEOSGeometry('MULTIPOLYGON EMPTY')) )[0] GlobalConfig._no_post_save_publishing = False # Bootstrap the admin group and user so we can use them beforehand update_or_create_group(name=UserGroupKey.SUPERADMIN, config_entity=global_config) # These users have the same name as their group update_or_create_user(username=UserGroupKey.SUPERADMIN, password='******', groups=[UserGroupKey.SUPERADMIN], is_super_user=True) # Bootstrap the global config Behaviors behavior_fixture = resolve_fixture("behavior", "behavior", BehaviorFixture, 'global', **kwargs) for behavior in behavior_fixture.behaviors(): update_or_create_behavior(behavior) # Boot strap the global config AttributeGroups attribute_group_fixture = resolve_fixture("behavior", "attribute_group", AttributeGroupFixture, 'global', **kwargs) for attribute_group in attribute_group_fixture.attribute_groups(): update_or_create_attribute_group(attribute_group) # Cartocss template storage create_media_subdir('styles') create_media_subdir('cartocss') # Sync the DBEntities to tables in the global schema global_config = initialize_global_config(**kwargs) for region_fixture in region_fixtures(): # Create the Behavior instances. # These can be defined at the Region scope, but most # are simply defined at default_behavior.py behavior_fixture = resolve_fixture("behavior", "behavior", BehaviorFixture, region_fixture.schema, **kwargs) return map( lambda behavior: update_or_create_behavior(behavior), behavior_fixture.behaviors())
def test_get_role_for_user(self): """Test that we extract the correct role key for a user.""" update_or_create_group('scag__or_cnty__admin', config_entity=self.scag) user_data = update_or_create_user(username='******', password='******', email='*****@*****.**', groups=['scag__or_cnty__admin']) self.assertEqual('admin', get_role_key_for_user(user_data['user']))
def on_config_entity_post_save_user(sender, **kwargs): config_entity = InstanceBundle.extract_single_instance(**kwargs) if config_entity._no_post_save_publishing: return if kwargs.get('created') and not config_entity.creator: # Set the ConfigEntity.creator to the default admin group user if it wasn't set by the API config_entity.creator = User.objects.get(username=UserGroupKey.SUPERADMIN) config_entity._no_post_save_publishing = True config_entity.save() config_entity._no_post_save_publishing = False # TODO these should be importable on top. Something is messed up user_fixture = resolve_fixture("user", "user", UserFixture, config_entity.schema(), config_entity=config_entity) # Get the ConfigEntityGroups of the ConfigEntity. GlobalConfig uses SuperAdmin as its Group config_entity_groups = config_entity.config_entity_groups() if \ not isinstance(config_entity, GlobalConfig) else \ [Group.objects.get(name=UserGroupKey.SUPERADMIN)] # Find all existing users of all ConfigEntity Groups of the ConfigEntity # Note that we use values() instead of all() to get dicts with just needed fields instead of model instances # TODO remove username from here once all users have emails. update_or_create_user() checks username for uniquess presently existing_user_dicts = flat_map( lambda group: group.user_set.all().values('email', 'username'), config_entity_groups ) # Combine the existing users with the fixtures, giving the former preference. We favor # what's in the database because the user might have updated their profile # Only accept fixture users not matching users in the db (by email) existing_emails = map(lambda existing_user_dict: existing_user_dict['email'], existing_user_dicts) logger.debug("Found existing users %s" % ', '.join(existing_emails)) new_fixture_users = filter(lambda fixture_user: fixture_user['email'] not in existing_emails, user_fixture.users()) if len(new_fixture_users) > 0: logger.debug("Found new fixture users %s" % ', '.join(map(lambda fixture_user: fixture_user['email'], new_fixture_users))) user_dicts = existing_user_dicts + new_fixture_users # Update or create each user. This will create users of new fixtures and run post-save processing # on both existing and new. for user_dict in user_dicts: update_or_create_user(**user_dict) reset_queries()
def test_view_users(self): """Test that an admin user can view a list of users to manage.""" update_or_create_group('scag__admin', self.scag, superiors=['superadmin']) update_or_create_group('scag__or_cnty__admin', self.orange, superiors=['superadmin', 'scag__admin']) update_or_create_group('scag__or_cnty__lgnhls__manager', self.laguna, superiors=['superadmin', 'scag__admin', 'scag__or_cnty__admin']) update_or_create_user(username='******', password='******', email='*****@*****.**', api_key=None, groups=['scag__or_cnty__admin'], is_super_user=False) update_or_create_user(username='******', password='******', email='*****@*****.**', api_key=None, groups=['scag__or_cnty__lgnhls__manager'], is_super_user=False) self.client.get('/footprint/api/v1/user/?format=json&[email protected]&password=test_oc_admin@uf') resp = self.client.get('/footprint/users/') self.assertEqual(len(resp.context['users']), 1) self.assertEqual(len(resp.context['users']['Laguna Hills']), 1) self.assertEqual(resp.context['users']['Laguna Hills'][0]['user'].username, 'test_lh_manager') self.assertEqual(resp.context['users']['Laguna Hills'][0]['role'], 'Manager')
def setUp(self, test_patch): self.global_config = globalSetup() self.scag = Region.objects.create( name='SCAG', key='scag_dm', scope='scag_dm', bounds='MULTIPOLYGON (((-20037508.3399999998509884 -20037508.3399999998509884, -20037508.3399999998509884 20037508.3399999998509884, \ 20037508.3399999998509884 20037508.3399999998509884, 20037508.3399999998509884 -20037508.3399999998509884, \ -20037508.3399999998509884 -20037508.3399999998509884)))', parent_config_entity=self.global_config ) update_or_create_group('admin', config_entity=self.scag, superiors=['superadmin']) update_or_create_user(username='******', password='******', email='*****@*****.**', api_key=None, groups=['admin']) self.test_admin = User.objects.get(username='******') self.client = Client()
def test_get_role_for_user(self): """Test that we extract the correct role key for a user.""" update_or_create_group('scag__or_cnty__admin', config_entity=self.scag) user_data = update_or_create_user( username='******', password='******', email='*****@*****.**', groups=['scag__or_cnty__admin']) self.assertEqual('admin', get_role_key_for_user(user_data['user']))
def test_update_user(self): self.client.get( '/footprint/api/v1/user/?format=json&[email protected]&password=test_superadmin_user@uf' ) update_or_create_group('admin', superiors=['superadmin']) update_or_create_group('manager', superiors=['admin']) update_or_create_group('user', superiors=['admin']) update_or_create_group('scag__or_cnty__admin', self.orange, superiors=['superadmin']) update_or_create_user(username='******', password='******', email='*****@*****.**', first_name='Test', last_name='User', api_key=None, groups=['scag__or_cnty__admin'], is_super_user=False) user_to_update = User.objects.get(username='******') resp = self.client.post( '/footprint/user/{}/'.format(user_to_update.id), dict(username='******', raw_password='******', confirm_password='******', email='*****@*****.**', role='admin', config_entity=self.orange.id, first_name='Changed'), follow=True) self.assertEqual(200, resp.status_code) match = re.search(r'User successfully updated', resp.content) self.assertTrue(bool(match)) self.assertEqual('Changed', User.objects.get(id=user_to_update.id).first_name)
def test_view_users(self): """Test that an admin user can view a list of users to manage.""" update_or_create_group('scag__admin', self.scag, superiors=['superadmin']) update_or_create_group('scag__or_cnty__admin', self.orange, superiors=['superadmin', 'scag__admin']) update_or_create_group( 'scag__or_cnty__lgnhls__manager', self.laguna, superiors=['superadmin', 'scag__admin', 'scag__or_cnty__admin']) update_or_create_user(username='******', password='******', email='*****@*****.**', api_key=None, groups=['scag__or_cnty__admin'], is_super_user=False) update_or_create_user(username='******', password='******', email='*****@*****.**', api_key=None, groups=['scag__or_cnty__lgnhls__manager'], is_super_user=False) self.client.get( '/footprint/api/v1/user/?format=json&[email protected]&password=test_oc_admin@uf' ) resp = self.client.get('/footprint/users/') self.assertEqual(len(resp.context['users']), 1) self.assertEqual(len(resp.context['users']['Laguna Hills']), 1) self.assertEqual( resp.context['users']['Laguna Hills'][0]['user'].username, 'test_lh_manager') self.assertEqual(resp.context['users']['Laguna Hills'][0]['role'], 'Manager')
def globalSetup(): # Bootstrap GlobalConfig._no_post_save_publishing = True GlobalConfig.objects.update_or_create( key=Keys.GLOBAL_CONFIG_KEY, defaults=dict(bounds=GEOSGeometry('MULTIPOLYGON EMPTY')) ) GlobalConfig._no_post_save_publishing = False update_or_create_group('superadmin') update_or_create_user(username='******', password='******', email='*****@*****.**', api_key=None, groups=['superadmin'], is_super_user=True) return GlobalConfig.objects.update_or_create( key='global', defaults=dict( name='Global Config', scope='global', bounds='MULTIPOLYGON (((-20037508.3399999998509884 -20037508.3399999998509884, -20037508.3399999998509884 20037508.3399999998509884, \ 20037508.3399999998509884 20037508.3399999998509884, 20037508.3399999998509884 -20037508.3399999998509884, \ -20037508.3399999998509884 -20037508.3399999998509884)))' ) )[0]