示例#1
0
class LogEntryApiAttrs(ObjectAttrs):
    action = fields.Str(attribute='action_name')
    change_timestamp = fields.DateTime()
    change_user_id = fields.Str()
    difference = fields.Method('get_difference')
    message = fields.Str()
    new_value = fields.Method('get_new_value')
    row_id = fields.Int()
    table_name = fields.Str()

    class Meta:
        strict = True
        ordered = True
        object_type = 'history'
        api_path = '/histories'
        url_template = '{api_url}/histories/{ident}'
        model = 'histories.LogEntry'

    def get_difference(self, obj):
        try:
            data = json.loads(obj.difference)
        except ValueError:
            data = {}
        if obj.action_name == 'INSERT':
            for key, val in data.items():
                if isinstance(val, list) and len(val) == 2:
                    data[key] = val[1]
        return data

    def get_new_value(self, obj):
        return self.get_difference(obj)
示例#2
0
class HistoryApiAttrs(ObjectAttrs):
    action = fields.Str()
    change_timestamp = fields.DateTime()
    change_user_id = fields.Str()
    difference = fields.Method('get_difference')
    message = fields.Str()
    new_value = fields.Method('get_new_value')
    row_id = fields.Int()
    table_name = fields.Str()

    class Meta:
        strict = True
        ordered = True
        object_type = 'history'
        api_path = '/histories'
        url_template = '{api_url}/histories/{ident}'
        model = 'histories.History'

    def get_difference(self, obj):
        difference = json.loads(obj.difference) or {}
        if 'values_changed' in difference:
            if "root['password']" in difference['values_changed']:
                difference['values_changed']["root['password']"][
                    'new_value'] = "********"
                difference['values_changed']["root['password']"][
                    'old_value'] = "********"
        return difference

    def get_new_value(self, obj):
        new_value = obj.new_value if isinstance(
            obj.new_value, dict) else json.loads(obj.new_value)
        if new_value and 'password' in new_value:
            new_value['password'] = "******"
        return new_value
class InstitutionXMLSerializer(ExtSchema):
    id = fields.Integer()
    url = fields.Url(attribute='frontend_absolute_url')
    type = fields.Function(
        lambda organization: organization.get_institution_type_display())
    title = TranslatedStr()
    abbreviation = fields.Str()
    epuap = fields.Str()
    website = fields.Url()
    created = fields.DateTime(format='iso8601')
    modified = fields.DateTime(format='iso8601')

    postal_code = fields.Str()
    city = fields.Str()
    street = fields.Str()
    street_number = fields.Str()
    street_type = fields.Str()
    flat_number = fields.Str()

    email = fields.Str()
    tel = fields.Str(data_key='phone_number')

    regon = fields.Str()

    published_datasets_count = fields.Method('get_published_datasets_count')
    published_resources_count = fields.Method('get_published_resources_count')

    def get_published_datasets_count(self, organization):
        return self.context['published_datasets_count']

    def get_published_resources_count(self, organization):
        return self.context['published_resources_count']
示例#4
0
class ShowcaseApiAttrs(ObjectAttrs, HighlightObjectMixin):
    category = fields.Str()
    category_name = fields.Method('get_category_name')
    slug = TranslatedStr()
    title = TranslatedStr()
    notes = TranslatedStr()
    author = fields.Str(faker_type='firstname')
    url = fields.Str(faker_type='url')
    image_url = fields.Str(faker_type='image_url')
    image_thumb_url = fields.Str(faker_type='image_thumb_url')
    image_alt = TranslatedStr()
    illustrative_graphics_url = fields.Str()
    illustrative_graphics_alt = TranslatedStr()
    followed = fields.Boolean(faker_type='boolean')
    keywords = KeywordsList(TranslatedStr(), faker_type='tagslist')
    views_count = fields.Integer(faker_type='integer')
    modified = fields.Str(faker_type='datetime')
    created = fields.Str(faker_type='datetime')
    has_image_thumb = fields.Bool()
    main_page_position = fields.Int()
    external_datasets = fields.Nested(ExternalDataset, many=True)
    # application showcase fields:
    is_mobile_app = fields.Bool()
    is_mobile_app_name = fields.Method('get_is_mobile_app_name')
    is_desktop_app = fields.Bool()
    is_desktop_app_name = fields.Method('get_is_desktop_app_name')
    mobile_apple_url = fields.URL()
    mobile_google_url = fields.URL()
    desktop_linux_url = fields.URL()
    desktop_macos_url = fields.URL()
    desktop_windows_url = fields.URL()

    license_type = fields.Str()
    license_type_name = fields.Method('get_license_type_name')
    # api use showcase fields:
    file_url = fields.Str()

    class Meta:
        relationships_schema = ShowcaseApiRelationships
        object_type = 'showcase'
        url_template = '{api_url}/showcases/{ident}'
        model = 'showcases.Showcase'
        ordered = True

    def get_is_desktop_app_name(self, obj):
        return str(_('Desktop App')) if obj.is_desktop_app else ''

    def get_is_mobile_app_name(self, obj):
        return str(_('Mobile App')) if obj.is_mobile_app else ''

    def get_category_name(self, obj):
        return str(Showcase.CATEGORY_NAMES.get(obj.showcase_category, ''))

    def get_license_type_name(self, obj):
        return str(Showcase.LICENSE_TYPE_NAMES.get(obj.license_type, ''))
示例#5
0
class ResourceApiAttrs(ObjectAttrs, HighlightObjectMixin):
    title = TranslatedStr()
    description = TranslatedStr()
    category = fields.Str()
    format = fields.Str()
    media_type = fields.Str(
        attribute='type'
    )  # https://jsonapi.org/format/#document-resource-object-fields
    visualization_types = ListWithoutNoneStrElement(fields.Str())
    downloads_count =\
        fields.Function(
            lambda obj: obj.computed_downloads_count if is_enabled('S16_new_date_counters.be') else obj.downloads_count)
    openness_score = fields.Integer()
    views_count =\
        fields.Function(
            lambda obj: obj.computed_views_count if is_enabled('S16_new_date_counters.be') else obj.views_count)
    modified = fields.DateTime()
    created = fields.DateTime()
    verified = fields.DateTime()
    data_date = fields.Date()
    file_url = fields.Str()
    file_size = fields.Integer()
    csv_file_url = fields.Str()
    csv_file_size = fields.Integer()
    jsonld_file_url = fields.Str()
    jsonld_file_size = fields.Integer()
    jsonld_download_url = fields.Str()
    download_url = fields.Str()
    csv_download_url = fields.Str()
    link = fields.Str()
    data_special_signs = fields.Nested(SpecialSignSchema,
                                       data_key='special_signs',
                                       many=True)
    is_chart_creation_blocked = fields.Bool()
    if is_enabled('S35_high_value_data.be'):
        has_high_value_data = fields.Boolean()
    if is_enabled('S37_resources_admin_region_data.be'):
        regions = fields.Method('get_regions')
    if is_enabled('S40_new_file_model.be'):
        files = fields.Method('get_files')

    class Meta:
        relationships_schema = ResourceApiRelationships
        object_type = 'resource'
        api_path = 'resources'
        url_template = '{api_url}/resources/{ident}'
        model = 'resources.Resource'

    def get_regions(self, res):
        return RegionSchema(many=True).dump(
            getattr(res, 'all_regions', res.regions))

    def get_files(self, res):
        return ResourceFileSchema(many=True).dump(
            getattr(res, 'all_files', res.files))
示例#6
0
class DatasetXMLSerializer(ExtSchema):
    id = fields.Integer()
    url = fields.Url(attribute='frontend_absolute_url')
    title = TranslatedStr()
    notes = TranslatedStr()
    keywords = fields.Function(lambda dataset: (
        tag.name for tag in getattr(dataset, f'tags_{get_language()}')))
    categories = fields.Nested(DatasetCategoryAttr, many=True)
    update_frequency = TransUpdateFreqField()
    created = fields.DateTime()
    verified = fields.DateTime()
    views_count = fields.Int(attribute='computed_views_count')
    downloads_count = fields.Int(attribute='computed_downloads_count')
    published_resources_count = fields.Int(
        attribute='published_resources__count')
    license = fields.Str(attribute='license_name')
    conditions = fields.Method('get_conditions')
    organization = fields.Method('get_organization')
    resources = fields.Method('get_resources')

    source = fields.Nested(SourceXMLSchema)

    def get_conditions(self, dataset):
        conditions = _(
            'This dataset is public information, it can be reused under the following conditions: '
        )
        terms = [
            str(
                dataset._meta.get_field(
                    'license_condition_modification').verbose_name)
            if dataset.license_condition_modification else '',
            str(
                dataset._meta.get_field('license_condition_source').
                verbose_name) if dataset.license_condition_source else '',
            dataset.license_condition_db_or_copyrighted,
            dataset.license_condition_personal_data
        ]
        return conditions + '\n'.join([term for term in terms if term])

    def get_organization(self, dataset):
        context = {
            'published_datasets_count':
            dataset.organization_published_datasets__count,
            'published_resources_count':
            dataset.organization_published_resources__count,
        }
        return InstitutionXMLSerializer(many=False, context=context).dump(
            dataset.organization)

    def get_resources(self, dataset):
        return ResourceXMLSerializer(many=True).dump(
            dataset.published_resources)
class InstitutionCSVMetadataSerializer(ExtSchema):
    organization_url = fields.Url(attribute='frontend_absolute_url',
                                  data_key=_('Organization URL'))
    organization_type = fields.Function(
        lambda obj: obj.get_institution_type_display(),
        data_key=_('Institution type'))
    organization_title = TranslatedStr(attribute='title', data_key=_('Name'))
    organization_abbr_title = TranslatedStr(attribute='abbreviation',
                                            data_key=_('Abbreviation'),
                                            default='')
    organization_regon = fields.Str(data_key=_('REGON'), attribute='regon')
    organization_epuap = fields.Str(attribute='epuap',
                                    data_key=_('EPUAP'),
                                    default='')
    organization_website = fields.Url(attribute='website',
                                      data_key=_('Website'))
    organization_created = fields.DateTime(attribute='created',
                                           data_key=_('Organization created'),
                                           format='iso8601')
    organization_modified = fields.DateTime(
        attribute='modified',
        data_key=_('Organization modified'),
        format='iso8601')
    organization_datasets_count = fields.Method(
        'get_published_datasets_count', data_key=_('Number of datasets'))
    organization_resources_count = fields.Method(
        'get_published_resources_count',
        data_key=_('Number of organization resources'))
    organization_postal_code = fields.Str(attribute='postal_code',
                                          data_key=_('Postal code'))
    organization_city = fields.Str(attribute='city', data_key=_('City'))
    organization_street_type = fields.Str(attribute='street_type',
                                          data_key=_('Street type'))
    organization_street = fields.Str(attribute='street', data_key=_('Street'))
    organization_street_number = fields.Str(attribute='street_number',
                                            data_key=_('Street number'))
    organization_flat_number = fields.Str(attribute='flat_number',
                                          data_key=_('Flat number'))
    organization_email = fields.Email(attribute='email', data_key=_('Email'))
    organization_phone_number = fields.Str(attribute='tel',
                                           data_key=_('Phone'))

    class Meta:
        ordered = True

    def get_published_datasets_count(self, organization):
        return self.context['published_datasets_count']

    def get_published_resources_count(self, organization):
        return self.context['published_resources_count']
class UserCSVSerializer(CSVSerializer):
    id = api_fields.Int(data_key=_('id'), required=True, example=77)
    email = api_fields.Email(data_key=_('Email'),
                             default='',
                             required=True,
                             example='*****@*****.**')
    fullname = api_fields.Str(data_key=_('Full name'),
                              default='',
                              example='Jan Kowalski')
    official_phone = api_fields.Method('get_phone',
                                       data_key=_('Official phone'),
                                       example='+481234567890')
    role = api_fields.Method('get_role',
                             data_key=_('Role'),
                             default='',
                             example='+481234567890')
    state = api_fields.Str(
        data_key=_('State'),
        required=True,
        example='active',
        description="Allowed values: 'active', 'inactive' or 'blocked'")
    institution = api_fields.Method('get_institutions',
                                    data_key=_('Institution'),
                                    example='Ministerstwo Cyfryzacji')

    def get_phone(self, obj):
        if obj.phone:
            phone = obj.phone
            if obj.phone_internal:
                phone += f'.{obj.phone_internal}'
            return phone
        return ''

    def get_role(self, obj):
        if obj.is_superuser:
            return _('Admin')
        elif obj.is_staff:
            return _('Editor')
        else:
            return _('User')

    def get_institutions(self, obj):
        return ','.join(str(org.id) for org in obj.organizations.all())

    class Meta:
        ordered = True
        model = 'users.User'
示例#9
0
class SubscriptionAttrs(schemas.ExtSchema):
    enable_notifications = fields.Boolean()
    created = fields.DateTime()
    modified = fields.DateTime()
    name = fields.Str(attribute='display_name')
    object_name = fields.Str(attribute='watcher.object_name')
    object_url = fields.Method('get_object_url')
    customfields = fields.Dict()

    def get_object_url(self, obj):
        if obj.watcher.watcher_type == 'model':
            instance = obj.watcher.obj
            return instance.api_url
        return ''
示例#10
0
class ShowcaseProposalCSVSerializer(CSVSerializer):
    id = fields.Int(data_key='id', required=True, example=77)
    category_name = fields.Str(data_key=_('Category'), example='Aplikacja')
    title = fields.Str(data_key=_('Name'), example='Propozycja aplikacji')
    notes = fields.Str(data_key=_('Notes'), default='', example='opis...')
    url = fields.Str(data_key=_('App URL'),
                     default='',
                     example='http://example.com')
    author = fields.Str(data_key=_('Author'),
                        default='',
                        example='Jan Kowalski')
    applicant_email = fields.Email(data_key=_('applicant email'),
                                   default='',
                                   required=False,
                                   example='*****@*****.**')
    keywords = fields.Str(data_key=_('keywords'),
                          attribute='keywords_as_str',
                          default='',
                          example='tag1,tag2,tag3')
    report_date = fields.Date(data_key=_('report date'))
    decision_date = fields.Date(data_key=_('decision date'), default=None)
    comment = fields.Str(data_key=_('comment'),
                         example='komentarz...',
                         default='')
    datasets = fields.Method('get_datasets',
                             data_key=_('datasets'),
                             example='998,999',
                             default='')
    external_datasets = fields.Raw(data_key=_('external datasets'),
                                   example='[]')
    showcase = fields.Int(data_key=_('Showcase'),
                          attribute='showcase.id',
                          default=None)

    class Meta:
        ordered = True
        model = 'showcases.ShowcaseProposal'

    def get_datasets(self, obj):
        return ','.join(str(x.id) for x in obj.datasets.order_by('id'))
示例#11
0
class CommonObjectApiAttrs(ObjectAttrs, HighlightObjectMixin):
    model = fields.Str()

    # common
    slug = TranslatedStr()
    title = TranslatedStr()
    notes = TranslatedStr()
    keywords = KeywordsList(TranslatedStr(), faker_type='tagslist')
    modified = fields.DateTime()
    created = fields.DateTime()
    verified = fields.DateTime()
    categories = fields.Nested(Category, many=True)
    category = fields.Nested(Category)
    if is_enabled('S35_high_value_data.be'):
        has_high_value_data = fields.Boolean()

    # datasets
    source = fields.Nested(SourceSchema)

    # resources
    data_date = fields.Date()
    visualization_types = fields.List(fields.Str())

    # applications, showcases
    author = fields.Str()
    illustrative_graphics_alt = TranslatedStr()
    illustrative_graphics_url = fields.Str()
    image_alt = TranslatedStr()
    image_thumb_url = fields.Str()
    if is_enabled('S39_showcases.be'):
        showcase_category = fields.Str()
        showcase_category_name = fields.Method('get_showcase_category_name')
        showcase_types = fields.List(fields.Str())
        showcase_platforms = fields.List(fields.Str())

    # institutions
    abbreviation = fields.Str()
    institution_type = fields.Str()
    datasets_count = fields.Int(attribute='published_datasets_count')
    resources_count = fields.Int(attribute='published_resources_count')
    fax = fields.Str()
    tel = fields.Str()
    sources = fields.Nested(DataSourceAttr, many=True)

    # cms pages
    html_url = fields.Str()

    if is_enabled('S39_filter_by_geodata.be'):
        # regions
        region_id = fields.Int()
        hierarchy_label = TranslatedStr()
        bbox = fields.List(fields.List(fields.Float),
                           attribute='bbox.coordinates')
        regions = fields.Nested(RegionSchema, many=True)

    def get_showcase_category_name(self, obj):
        val = getattr(obj, 'showcase_category', None)
        if val:
            model = apps.get_model('showcases.Showcase')
            return str(model.CATEGORY_NAMES[val])
        return missing

    @staticmethod
    def self_api_url(data):
        try:
            api_url = getattr(settings, 'API_URL', 'https://api.dane.gov.pl')
            model = data.model
            obj_id = data.id
            slug = data['slug'][get_language()]
            full_url = f'{api_url}/{model}s/{obj_id},{slug}'
        except AttributeError:
            full_url = None
        return full_url

    class Meta:
        relationships_schema = CommonObjectRelationships
        object_type = 'common'
        api_path = 'search'
示例#12
0
class DatasetResourcesCSVSerializer(CSVSerializer):
    dataset_url = fields.Url(attribute='frontend_absolute_url',
                             data_key=_('Dataset URL'))
    dataset_title = TranslatedStr(attribute='title', data_key=_('Title'))
    dataset_description = TranslatedStr(attribute='notes', data_key=_('Notes'))
    dataset_keywords = fields.Function(lambda obj: ', '.join(
        (tag.name for tag in getattr(obj, f'tags_{get_language()}'))),
                                       data_key=_('Tag'))
    dataset_categories = fields.Function(lambda obj: ', '.join(
        (category.title_i18n for category in obj.categories.all())),
                                         data_key=_('Category'))
    dataset_update_frequency = fields.Str(attribute='frequency_display',
                                          data_key=_('Update frequency'))
    dataset_created = fields.DateTime(attribute='created',
                                      data_key=_('Dataset created'),
                                      format='iso8601')
    dataset_verified = fields.DateTime(attribute='verified',
                                       data_key=_('Dataset verified'),
                                       format='iso8601')
    views_count = fields.Int(attribute='computed_views_count',
                             data_key=_("Dataset views count"))
    downloads_count = fields.Int(attribute='computed_downloads_count',
                                 data_key=_('Dataset downloads count'))
    dataset_resources_count = fields.Int(
        attribute='published_resources__count', data_key=_('Number of data'))
    dataset_conditions = fields.Method('get_dataset_conditions',
                                       data_key=_('Terms of use'))
    dataset_license = fields.Str(attribute='license_name',
                                 data_key=_('License'))
    dataset_source = fields.Nested(SourceXMLSchema,
                                   attribute='source',
                                   data_key=_('source'))
    organization = fields.Method('get_organization')
    resources = fields.Nested(ResourceCSVMetadataSerializer,
                              many=True,
                              attribute='published_resources')

    @ma.post_dump(pass_many=True)
    def unpack_nested_data(self, data, many, **kwargs):
        new_result_data = []
        for record in data:
            resources = record.pop('resources')
            organization = record.pop('organization')
            record.update(**organization)
            for resource in resources:
                tmp_record = record.copy()
                tmp_record.update(**resource)
                new_result_data.append(tmp_record)
        return new_result_data

    def get_dataset_conditions(self, dataset):
        conditions = _(
            'This dataset is public information, it can be reused under the following conditions: '
        )
        terms = [
            str(
                dataset._meta.get_field(
                    'license_condition_modification').verbose_name)
            if dataset.license_condition_modification else '',
            str(
                dataset._meta.get_field('license_condition_source').
                verbose_name) if dataset.license_condition_source else '',
            dataset.license_condition_db_or_copyrighted,
            dataset.license_condition_personal_data
        ]
        return conditions + '\n'.join([term for term in terms if term])

    @ma.post_dump(pass_many=False)
    def prepare_nested_data(self, data, **kwargs):
        source = data.get(_('source'))
        if source:
            source_str =\
                '{title_label}: {title}, {url_label}: {url},' \
                ' {last_import_label}: {last_import}, {frequency_label}: {frequency}'.format(
                    title=source['title'], title_label=_('name'),
                    url=source['url'], url_label=_('url'),
                    last_import=source['last_import_timestamp'], last_import_label=_('last import timestamp'),
                    frequency=source['update_frequency'], frequency_label=_('Update frequency')
                )
            data[_('source')] = source_str
        data[_('Notes')] = strip_tags(data[_('Notes')])
        return data

    def get_organization(self, dataset):
        context = {
            'published_datasets_count':
            dataset.organization_published_datasets__count,
            'published_resources_count':
            dataset.organization_published_resources__count,
        }
        return InstitutionCSVMetadataSerializer(
            many=False, context=context).dump(dataset.organization)

    def get_csv_headers(self):
        result = []
        for field_name, field in self.fields.items():
            if field_name == 'organization':
                org_headers = [
                    org_field.data_key for org_field_name, org_field in
                    InstitutionCSVMetadataSerializer().fields.items()
                ]
                result.extend(org_headers)
            elif field_name == 'resources':
                res_headers = [
                    res_field.data_key for res_field_name, res_field in
                    field.schema.fields.items()
                ]
                result.extend(res_headers)
            else:
                header = field.data_key or field_name
                result.append(header)
        return result

    class Meta:
        ordered = True
class UserCSVSerializer(CSVSerializer):
    id = fields.Int(data_key=_('id'), required=True, example=77)
    email = fields.Email(data_key=_('Email'),
                         default='',
                         required=True,
                         example='*****@*****.**')
    fullname = fields.Str(data_key=_('Full name'),
                          default='',
                          example='Jan Kowalski')
    official_phone = fields.Method('get_phone',
                                   data_key=_('Official phone'),
                                   example='+481234567890')
    role = fields.Method('get_role',
                         data_key=_('Role'),
                         default='',
                         example='+481234567890')
    state = fields.Str(
        data_key=_('State'),
        required=True,
        example='active',
        description="Allowed values: 'active', 'inactive' or 'blocked'")
    institution = fields.Str(attribute='institutions_ids_list_as_str',
                             data_key=_('Institution'),
                             example='1,2')
    institution1 = fields.Str(attribute='institutions_ids_list_as_str',
                              data_key=_('Institution1'),
                              example='1,2')
    institution2 = fields.Int(attribute='agent_organization_id',
                              data_key=_('Institution2'),
                              example=1)
    is_academy_admin = fields.Method('get_is_academy_admin',
                                     data_key=_('Admin AOD'),
                                     example='Nie')
    is_agent = fields.Method('get_is_agent',
                             data_key=_('Agent'),
                             example='Nie')
    extra_agent_of = fields.Method('get_extra_agent_of',
                                   data_key=_('Extra agent'),
                                   example=1)
    is_labs_admin = fields.Method('get_is_labs_admin',
                                  data_key=_('Admin LOD'),
                                  example='Nie')
    is_official = fields.Method('get_is_official',
                                data_key=_('Official'),
                                example='Nie')
    is_staff = fields.Method('get_is_staff',
                             data_key=_('Editor'),
                             example='Nie')
    is_superuser = fields.Method('get_is_superuser',
                                 data_key=_('Admin'),
                                 example='Nie')
    last_login = fields.DateTime(data_key=_('Last login date'),
                                 example='2021-01-01T00:00:00Z',
                                 default=None)

    @staticmethod
    def get_yes():
        return _('Yes')

    @staticmethod
    def get_no():
        return _('No')

    def get_is_academy_admin(self, obj):
        return self.get_yes() if obj.is_academy_admin else self.get_no()

    def get_is_agent(self, obj):
        return self.get_yes() if obj.is_agent else self.get_no()

    def get_extra_agent_of(self, obj):
        return obj.extra_agent_of.id if obj.extra_agent_of else None

    def get_is_labs_admin(self, obj):
        return self.get_yes() if obj.is_labs_admin else self.get_no()

    def get_is_superuser(self, obj):
        return self.get_yes() if obj.is_superuser else self.get_no()

    def get_is_official(self, obj):
        return self.get_yes() if obj.is_official else self.get_no()

    def get_is_staff(self, obj):
        return self.get_yes() if obj.is_staff else self.get_no()

    def get_phone(self, obj):
        if obj.phone:
            phone = obj.phone
            if obj.phone_internal:
                phone += f'.{obj.phone_internal}'
            return phone
        return ''

    def get_role(self, obj):
        if obj.is_superuser:
            return _('Admin')
        elif obj.is_staff:
            return _('Editor')
        else:
            return _('User')

    class Meta:
        ordered = True
        model = 'users.User'
        fields = ('id', 'email', 'fullname', 'official_phone', 'is_staff',
                  'is_official', 'is_superuser', 'is_academy_admin',
                  'is_labs_admin', 'is_agent', 'extra_agent_of', 'state',
                  'institution1', 'institution2', 'last_login')