Ejemplo n.º 1
0
Archivo: core.py Proyecto: sf2ne/SHARE
class ShareUser(AbstractBaseUser, PermissionsMixin):
    id = models.AutoField(primary_key=True)
    username = models.TextField(
        _('username'),
        unique=True,
        help_text=_('Required. 30 characters or fewer. Letters, digits and @/./+/-/_ only.'),
        validators=[
            validators.MaxLengthValidator(64),
            validators.RegexValidator(
                r'^[\w.@+-]+$',
                _('Enter a valid username. This value may contain only '
                  'letters, numbers ' 'and @/./+/-/_ characters.')
            ),
        ],
        error_messages={
            'unique': _("A user with that username already exists."),
        },
    )
    first_name = models.TextField(_('first name'), validators=[validators.MaxLengthValidator(64)], blank=True)
    last_name = models.TextField(_('last name'), validators=[validators.MaxLengthValidator(64)], blank=True)
    email = models.EmailField(_('email address'), blank=True)
    gravatar = ShareURLField(blank=True)
    time_zone = models.TextField(validators=[validators.MaxLengthValidator(100)], blank=True)
    locale = models.TextField(validators=[validators.MaxLengthValidator(100)], blank=True)
    is_staff = models.BooleanField(
        _('staff status'),
        default=False,
        help_text=_('Designates whether the user can log into this admin site.'),
    )
    is_active = models.BooleanField(
        _('active'),
        default=True,
        help_text=_(
            'Designates whether this user should be treated as active. '
            'Unselect this instead of deleting accounts.'
        ),
    )
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)
    robot = models.TextField(validators=[validators.MaxLengthValidator(40)], blank=True)
    long_title = models.TextField(validators=[validators.MaxLengthValidator(100)], blank=True)
    home_page = ShareURLField(blank=True)

    def get_short_name(self):
        return self.robot if self.is_robot else self.username

    def get_full_name(self):
        return '{} {}'.format(self.first_name, self.last_name)

    @property
    def is_robot(self):
        return self.robot != ''

    objects = ShareUserManager()

    USERNAME_FIELD = 'username'

    class Meta:
        verbose_name = _('Share user')
        verbose_name_plural = _('Share users')
Ejemplo n.º 2
0
class Venue(ShareObject):
    name = models.TextField(blank=True)
    venue_type = ShareURLField(blank=True)
    location = ShareURLField(blank=True)
    community_identifier = ShareURLField(blank=True)

    def __str__(self):
        return self.name
Ejemplo n.º 3
0
class Award(ShareObject):
    # ScholarlyArticle has an award object
    # it's just a text field, I assume our 'description' covers it.
    award = ShareURLField(blank=True)
    description = models.TextField(blank=True)
    url = ShareURLField(blank=True)
    entities = ShareManyToManyField('Entity', through='ThroughAwardEntities')

    def __str__(self):
        return self.description
Ejemplo n.º 4
0
class AbstractCreativeWork(ShareObject, metaclass=TypedShareObjectMeta):
    title = models.TextField(blank=True)
    description = models.TextField(blank=True)

    contributors = ShareManyToManyField(Person, through='Contributor')

    awards = ShareManyToManyField(Award, through='ThroughAwards')
    venues = ShareManyToManyField(Venue, through='ThroughVenues')

    links = ShareManyToManyField('Link', through='ThroughLinks')

    funders = ShareManyToManyField('Funder', through='Association')
    publishers = ShareManyToManyField('Publisher', through='Association')
    institutions = ShareManyToManyField('Institution', through='Association')
    organizations = ShareManyToManyField('Organization', through='Association')

    subject = ShareForeignKey(Tag,
                              related_name='subjected_%(class)s',
                              null=True)
    # Note: Null allows inserting of None but returns it as an empty string
    tags = ShareManyToManyField(Tag,
                                related_name='tagged_%(class)s',
                                through='ThroughTags')
    date_created = models.DateTimeField(null=True, db_index=True)
    date_published = models.DateTimeField(null=True, db_index=True)
    date_updated = models.DateTimeField(null=True, db_index=True)
    free_to_read_type = ShareURLField(blank=True, db_index=True)
    free_to_read_date = models.DateTimeField(null=True, db_index=True)

    rights = models.TextField(blank=True, null=True, db_index=True)
    language = models.TextField(blank=True, null=True, db_index=True)

    def __str__(self):
        return self.title
Ejemplo n.º 5
0
class Person(ShareObject):
    family_name = models.TextField(blank=True, db_index=True)  # last
    given_name = models.TextField(blank=True, db_index=True)  # first
    additional_name = models.TextField(blank=True,
                                       db_index=True)  # can be used for middle
    suffix = models.TextField(blank=True, db_index=True)

    emails = ShareManyToManyField(Email, through='PersonEmail')
    affiliations = ShareManyToManyField('Entity', through='Affiliation')
    # this replaces "authority_id" and "other_identifiers" in the diagram
    identifiers = ShareManyToManyField(Identifier,
                                       through='ThroughIdentifiers')
    location = models.TextField(blank=True)
    url = ShareURLField(blank=True)

    def __str__(self):
        return self.get_full_name()

    def get_full_name(self):
        return ' '.join(x for x in [
            self.given_name, self.family_name, self.additional_name,
            self.suffix
        ] if x)

    class Meta:
        verbose_name_plural = 'People'
        index_together = (('family_name', 'given_name', 'additional_name',
                           'suffix'))
Ejemplo n.º 6
0
class AgentIdentifier(ShareObject):
    """Unique identifier (in IRI form) for an agent."""
    uri = ShareURLField(unique=True)
    host = models.TextField(editable=False)
    scheme = models.TextField(editable=False)
    agent = ShareForeignKey('AbstractAgent', related_name='identifiers')

    # objects = FilteredEmailsManager()
    # objects_unfiltered = models.Manager()

    @classmethod
    def normalize(self, node, graph):
        try:
            ret = IRILink().execute(node.attrs['uri'])
        except InvalidIRI as e:
            logger.warning('Discarding invalid identifier %s with error %s',
                           node.attrs['uri'], e)
            graph.remove(node)
            return

        if node.attrs['uri'] != ret['IRI']:
            logger.debug('Normalized %s to %s', node.attrs['uri'], ret['IRI'])

        node.attrs = {
            'uri': ret['IRI'],
            'host': ret['authority'],
            'scheme': ret['scheme'],
        }

    def __repr__(self):
        return '<{}({}, {})>'.format(self.__class__.__name__, self.uri,
                                     self.agent_id)

    class Disambiguation:
        all = ('uri', )
Ejemplo n.º 7
0
class Subject(ShareObject):
    name = models.TextField()
    is_deleted = models.BooleanField(default=False)
    uri = ShareURLField(null=True, blank=True)
    taxonomy = models.ForeignKey(SubjectTaxonomy,
                                 editable=False,
                                 on_delete=models.CASCADE)
    parent = ShareForeignKey('Subject',
                             blank=True,
                             null=True,
                             related_name='children')
    central_synonym = ShareForeignKey('Subject',
                                      blank=True,
                                      null=True,
                                      related_name='custom_synonyms')

    @classmethod
    def normalize(cls, node, graph):
        edge = node.related('central_synonym')
        if edge and edge.related and edge.related.id == node.id:
            graph.remove_edge(edge)

    def save(self, *args, **kwargs):
        if self.id is not None and self.parent is not None:
            new_lineage = self.parent.lineage()
            if self in new_lineage:
                raise CyclicalTaxonomyError(
                    'Making {} a child of {} would cause a cycle!'.format(
                        self, self.parent))
        return super().save(*args, **kwargs)

    def lineage(self):
        query = '''
            WITH RECURSIVE lineage_chain(id, parent, depth, path, cycle) AS (
                    SELECT id, parent_id, 1, ARRAY[id], false FROM {table} WHERE id = %(id)s
                  UNION
                    SELECT {table}.id, {table}.parent_id, lineage_chain.depth + 1, path || {table}.id, {table}.id = ANY(path)
                    FROM lineage_chain JOIN {table} ON lineage_chain.parent = {table}.id
                    WHERE NOT cycle
            )
            SELECT {table}.* FROM {table} INNER JOIN lineage_chain ON {table}.id = lineage_chain.id ORDER BY lineage_chain.depth DESC
        '''.format(table=self._meta.db_table)
        lineage = list(
            self._meta.model.objects.raw(query, params={'id': self.id}))
        if lineage[0].parent is not None:
            raise CyclicalTaxonomyError(
                'Subject taxonomy cycle! {}'.format(lineage))
        return lineage

    def __str__(self):
        return self.name

    class Meta:
        unique_together = (('name', 'taxonomy'), ('uri', 'taxonomy'))

    class Disambiguation:
        all = ('name', 'central_synonym')
Ejemplo n.º 8
0
class Award(ShareObject):
    # ScholarlyArticle has an award object
    # it's just a text field, I assume our 'description' covers it.
    name = models.TextField(blank=True)
    description = models.TextField(blank=True)
    date = models.DateTimeField(blank=True, null=True)
    award_amount = models.PositiveIntegerField(blank=True, null=True)
    uri = ShareURLField(unique=True, blank=True, null=True)

    def __str__(self):
        return self.description

    matching_criteria = MatchByAttrs('uri')
Ejemplo n.º 9
0
class AbstractCreativeWork(ShareObject, metaclass=TypedShareObjectMeta):
    title = models.TextField(blank=True, help_text='')
    description = models.TextField(blank=True, help_text='')
    is_deleted = models.BooleanField(
        default=False,
        help_text=
        _('Determines whether or not this record will be discoverable via search.'
          ))
    date_published = models.DateTimeField(null=True)
    date_updated = models.DateTimeField(null=True)
    free_to_read_type = ShareURLField(blank=True)
    free_to_read_date = models.DateTimeField(null=True)
    rights = models.TextField(blank=True, null=True)
    language = models.TextField(
        blank=True,
        null=True,
        help_text=
        _('The ISO 3166-1 alpha-2 country code indicating the language of this record.'
          ))

    subjects = ShareManyToManyField(Subject,
                                    related_name='subjected_works',
                                    through='ThroughSubjects')
    tags = ShareManyToManyField(Tag,
                                related_name='tagged_works',
                                through='ThroughTags')

    related_agents = ShareManyToManyField('AbstractAgent',
                                          through='AbstractAgentWorkRelation')
    related_works = ShareManyToManyField('AbstractCreativeWork',
                                         through='AbstractWorkRelation',
                                         through_fields=('subject', 'related'),
                                         symmetrical=False)

    @classmethod
    def normalize(self, node, graph):
        for k, v in tuple(node.attrs.items()):
            if isinstance(v, str):
                node.attrs[k] = strip_whitespace(v)
                if node.attrs[k] == 'null':
                    node.attrs[k] = ''

    class Disambiguation:
        any = ('identifiers', )

    class Meta:
        db_table = 'share_creativework'

    def __str__(self):
        return self.title
Ejemplo n.º 10
0
class WorkIdentifier(ShareObject):
    """
    Unique identifier (in IRI form) for a creative work.
    """
    uri = ShareURLField(unique=True)
    host = models.TextField(editable=False)
    scheme = models.TextField(
        editable=False,
        help_text=
        _('A prefix to URI indicating how the following data should be interpreted.'
          ))
    creative_work = ShareForeignKey('AbstractCreativeWork',
                                    related_name='identifiers')

    # objects = FilteredEmailsManager()
    # objects_unfiltered = models.Manager()

    @classmethod
    def normalize(self, node, graph):
        try:
            ret = IRILink().execute(node.attrs['uri'])
        except InvalidIRI as e:
            logger.warning('Discarding invalid identifier %s with error %s',
                           node.attrs['uri'], e)
            graph.remove(node)
            return

        if ret['authority'] in {'issn', 'orcid.org'
                                } or ret['scheme'] in {'mailto'}:
            logger.warning(
                'Discarding %s %s as an invalid identifier for works',
                ret['authority'], ret['IRI'])
            graph.remove(node)
            return

        if node.attrs['uri'] != ret['IRI']:
            logger.debug('Normalized %s to %s', node.attrs['uri'], ret['IRI'])

        node.attrs = {
            'uri': ret['IRI'],
            'host': ret['authority'],
            'scheme': ret['scheme'],
        }

    def __repr__(self):
        return '<{}({}, {})>'.format(self.__class__.__name__, self.uri,
                                     self.creative_work_id)

    class Disambiguation:
        all = ('uri', )
Ejemplo n.º 11
0
class AgentIdentifier(ShareObject):
    """Unique identifier (in IRI form) for an agent."""
    uri = ShareURLField(unique=True)
    host = models.TextField(blank=True)
    scheme = models.TextField(blank=True)
    agent = ShareForeignKey('AbstractAgent', related_name='identifiers')

    # objects = FilteredEmailsManager()
    # objects_unfiltered = models.Manager()

    def __repr__(self):
        return '<{}({}, {})>'.format(self.__class__.__name__, self.uri, self.agent_id)

    matching_criteria = MatchByAttrs('uri')
Ejemplo n.º 12
0
class Entity(ShareObject, metaclass=TypedShareObjectMeta):
    url = ShareURLField(blank=True)
    name = models.TextField()
    location = models.TextField(blank=True)
    affiliations = ShareManyToManyField('Person', through='Affiliation')

    class Meta:
        verbose_name_plural = 'Entities'
        index_together = ((
            'type',
            'name',
        ))

    def __str__(self):
        return self.name
Ejemplo n.º 13
0
class WorkIdentifier(ShareObject):
    """
    Unique identifier (in IRI form) for a creative work.
    """
    uri = ShareURLField(unique=True)
    host = models.TextField(blank=True)
    scheme = models.TextField(blank=True, help_text=_('A prefix to URI indicating how the following data should be interpreted.'))
    creative_work = ShareForeignKey('AbstractCreativeWork', related_name='identifiers')

    # objects = FilteredEmailsManager()
    # objects_unfiltered = models.Manager()

    def __repr__(self):
        return '<{}({}, {})>'.format(self.__class__.__name__, self.uri, self.creative_work_id)

    matching_criteria = MatchByAttrs('uri')
Ejemplo n.º 14
0
class Award(ShareObject):
    # ScholarlyArticle has an award object
    # it's just a text field, I assume our 'description' covers it.
    name = models.TextField(blank=True)
    description = models.TextField(blank=True)
    date = models.DateTimeField(blank=True, null=True)
    award_amount = models.PositiveIntegerField(blank=True, null=True)
    uri = ShareURLField(unique=True, blank=True, null=True)

    @classmethod
    def normalize(self, node, graph):
        for k, v in tuple(node.attrs.items()):
            if isinstance(v, str):
                node.attrs[k] = strip_whitespace(v)

    def __str__(self):
        return self.description

    class Disambiguation:
        any = ('uri', )
Ejemplo n.º 15
0
class AbstractCreativeWork(ShareObject, metaclass=TypedShareObjectMeta):
    title = models.TextField(blank=True)
    description = models.TextField(blank=True)

    # Used to determine if something should be surfaced in ES or not
    # this may need to be renamed later
    is_deleted = models.BooleanField(default=False)

    contributors = ShareManyToManyField(Person, through='Contributor')

    awards = ShareManyToManyField(Award, through='ThroughAwards')
    venues = ShareManyToManyField(Venue, through='ThroughVenues')

    links = ShareManyToManyField('Link', through='ThroughLinks')

    funders = ShareManyToManyField('Funder', through='Association')
    publishers = ShareManyToManyField('Publisher', through='Association')
    institutions = ShareManyToManyField('Institution', through='Association')
    organizations = ShareManyToManyField('Organization', through='Association')

    subjects = ShareManyToManyField(Subject,
                                    related_name='subjected_%(class)s',
                                    through='ThroughSubjects')
    # Note: Null allows inserting of None but returns it as an empty string
    tags = ShareManyToManyField(Tag,
                                related_name='tagged_%(class)s',
                                through='ThroughTags')
    date_published = models.DateTimeField(null=True, db_index=True)
    date_updated = models.DateTimeField(null=True, db_index=True)
    free_to_read_type = ShareURLField(blank=True, db_index=True)
    free_to_read_date = models.DateTimeField(null=True, db_index=True)

    rights = models.TextField(blank=True, null=True, db_index=True)
    language = models.TextField(blank=True, null=True, db_index=True)

    def __str__(self):
        return self.title
Ejemplo n.º 16
0
class Institution(Entity):
    # TODO: ScholarlyArticle says this should be an Organization
    isni = ShareURLField(blank=True)
    ringgold = ShareURLField(blank=True)
Ejemplo n.º 17
0
class AbstractCreativeWork(ShareObject, metaclass=TypedShareObjectMeta):
    title = models.TextField(blank=True, help_text='')
    description = models.TextField(blank=True, help_text='')
    is_deleted = models.BooleanField(
        default=False,
        help_text=
        _('Determines whether or not this record will be discoverable via search.'
          ))
    date_published = models.DateTimeField(blank=True, null=True)
    date_updated = models.DateTimeField(blank=True, null=True)
    free_to_read_type = ShareURLField(blank=True)
    free_to_read_date = models.DateTimeField(blank=True, null=True)
    rights = models.TextField(blank=True, null=True)
    language = models.TextField(
        blank=True,
        null=True,
        help_text=
        _('The ISO 3166-1 alpha-2 country code indicating the language of this record.'
          ))

    subjects = ShareManyToManyField(Subject,
                                    related_name='subjected_works',
                                    through='ThroughSubjects')
    tags = ShareManyToManyField(Tag,
                                related_name='tagged_works',
                                through='ThroughTags')

    related_agents = ShareManyToManyField('AbstractAgent',
                                          through='AbstractAgentWorkRelation')
    related_works = ShareManyToManyField('AbstractCreativeWork',
                                         through='AbstractWorkRelation',
                                         through_fields=('subject', 'related'),
                                         symmetrical=False)

    @classmethod
    def normalize(self, node, graph):
        for k, v in tuple(node.attrs.items()):
            if isinstance(v, str):
                node.attrs[k] = strip_whitespace(v)
                if node.attrs[k] == 'null':
                    node.attrs[k] = ''

    class Disambiguation:
        any = ('identifiers', )

    class Meta(ShareObject.Meta):
        db_table = 'share_creativework'
        verbose_name_plural = 'Creative Works'

    def defrankenize(self, *_, im_really_sure_about_this=False):
        if not im_really_sure_about_this:
            raise ValueError('You have to be really sure about this')

        logger.info('Defrankenizing %r', self)

        with transaction.atomic():
            logger.info('Removing relations')
            for field in AbstractCreativeWork._meta.get_fields():
                if not field.one_to_many or field.name in ('changes',
                                                           'versions'):
                    continue

                logger.warning('Removing all %s', field.related_name)
                relation = getattr(self, field.get_accessor_name())
                num_deleted, stats = Change.objects.filter(
                    id__in=relation.values_list('change_id',
                                                flat=True)).delete()
                logger.warning('Deleted %d changes to remove %s', num_deleted,
                               field.related_name)

                assert num_deleted == stats.pop('share.Change', 0)

                if stats:
                    logger.error('Unexpectedly removed other rows, %r', stats)
                    raise ValueError(
                        'Unexpectedly removed other rows, {!r}'.format(stats))

            logger.info('Relations removed')
            self.administrative_change(is_deleted=True,
                                       title='Defrankenized work')

    def __str__(self):
        return self.title
Ejemplo n.º 18
0
class Identifier(ShareObject):
    # https://twitter.com/berniethoughts/
    url = ShareURLField()
    # https://twitter.com/
    base_url = ShareURLField()
Ejemplo n.º 19
0
class Funder(Entity):
    # TODO: ScholarlyArticle says this should be a DiscourseElement
    # http://purl.org/spar/deo/DiscourseElement
    # many fields are missing but seem extraneous to our purpose
    funder_region = ShareURLField(blank=True)
    community_identifier = ShareURLField(blank=True)