def get_context(self): context = super(OrganizationDashboardView, self).get_context() context['metrics'] = [{ # 'title': _('Data'), 'widgets': [ { 'title': _('Datasets'), 'metric': 'datasets', 'type': 'line', 'endpoint': 'datasets.list', 'args': {'org': self.organization} }, { 'title': _('Reuses'), 'metric': 'reuses', 'type': 'line', 'endpoint': 'reuses.list', 'args': {'org': self.organization} }, { 'title': _('Followers'), 'metric': 'followers', 'type': 'line', } ] }] return context
def notify_membership_response(org, request): if request.status == "accepted": subject = _('You are now a member of the organization "%(org)s"', org=org) template = "new_member" else: subject, template = _("Membership refused"), "membership_refused" mail.send(subject, request.user, template, org=org, request=request)
def notify_membership_response(org, request): if request.status == 'accepted': subject, template = _( 'You are now a member of the organization "%(org)s"', org=org), 'new_member' else: subject, template = _('Membership refused'), 'membership_refused' mail.send(subject, request.user, template, org=org, request=request)
def pre_validate(self, form): if self.data: if current_user.is_anonymous(): raise validators.ValidationError( _('You must be authenticated')) elif not admin_permission and current_user.id != self.data.id: raise validators.ValidationError( _('You can only set yourself as owner')) return True
def test_labelize(self): self.assertEqual(self.facet.labelize('label', True), 'label: {0}'.format(_('yes'))) self.assertEqual(self.facet.labelize('label', False), 'label: {0}'.format(_('no'))) self.assertEqual(self.facet.labelize('label', 'true'), 'label: {0}'.format(_('yes'))) self.assertEqual(self.facet.labelize('label', 'false'), 'label: {0}'.format(_('no')))
def pre_validate(self, form): if not self.data: return for tag in self.data: if not MIN_TAG_LENGTH <= len(tag) <= MAX_TAG_LENGTH: message = _('Tag "%(tag)s" must be between %(min)d and %(max)d characters long.', min=MIN_TAG_LENGTH, max=MAX_TAG_LENGTH, tag=tag) raise validators.ValidationError(message) if not RE_TAG.match(tag): message = _('Tag "%(tag)s" must be alphanumeric characters or symbols: -_.', tag=tag) raise validators.ValidationError(message)
def notify_new_follower(follow): if isinstance(follow.following, User): subject =_('%(user)s followed you', user=follow.follower) mail.send(subject, follow.following, 'new_follower', follow=follow) elif isinstance(follow.following, Organization): subject = _('%(user)s followed your organization', user=follow.follower) recipients = [m.user for m in follow.following.members] mail.send(subject, recipients, 'new_follower_org', follow=follow) elif isinstance(follow.following, Dataset): pass elif isinstance(follow.following, Reuse): pass
def recent_feed(): feed = AtomFeed(_('Last datasets'), feed_url=request.url, url=request.url_root) datasets = (Dataset.objects.visible().order_by('-created_at') .limit(current_site.feed_size)) for dataset in datasets: author = None if dataset.organization: author = { 'name': dataset.organization.name, 'uri': url_for('organizations.show', org=dataset.organization.id, _external=True), } elif dataset.owner: author = { 'name': dataset.owner.fullname, 'uri': url_for('users.show', user=dataset.owner.id, _external=True), } feed.add(dataset.title, render_template('dataset/feed_item.html', dataset=dataset), content_type='html', author=author, url=url_for('datasets.show', dataset=dataset.id, _external=True), updated=dataset.last_modified, published=dataset.created_at) return feed.get_response()
def send_frequency_reminder(self): # We exclude irrelevant frequencies. frequencies = [f for f in UPDATE_FREQUENCIES.keys() if f not in ('unknown', 'realtime', 'punctual')] now = datetime.now() reminded_orgs = {} reminded_people = [] allowed_delay = current_app.config['DELAY_BEFORE_REMINDER_NOTIFICATION'] for org in Organization.objects.visible(): outdated_datasets = [] for dataset in Dataset.objects.filter( frequency__in=frequencies, organization=org).visible(): if dataset.next_update + timedelta(days=allowed_delay) < now: dataset.outdated = now - dataset.next_update dataset.frequency_str = UPDATE_FREQUENCIES[dataset.frequency] outdated_datasets.append(dataset) if outdated_datasets: reminded_orgs[org] = outdated_datasets for reminded_org, datasets in reminded_orgs.iteritems(): print(u'{org.name} will be emailed for {datasets_nb} datasets'.format( org=reminded_org, datasets_nb=len(datasets))) recipients = [m.user for m in reminded_org.members] reminded_people.append(recipients) subject = _('You need to update some frequency-based datasets') mail.send(subject, recipients, 'frequency_reminder', org=reminded_org, datasets=datasets) print('{nb_orgs} orgs concerned'.format(nb_orgs=len(reminded_orgs))) reminded_people = flatten(reminded_people) print('{nb_emails} people contacted ({nb_emails_twice} twice)'.format( nb_emails=len(reminded_people), nb_emails_twice=len(reminded_people) - len(Set(reminded_people)))) print('Done')
def recent_feed(): feed = AtomFeed(_('Last reuses'), feed_url=request.url, url=request.url_root) reuses = Reuse.objects.visible().order_by('-created_at').limit(15) for reuse in reuses: author = None if reuse.organization: author = { 'name': reuse.organization.name, 'uri': url_for('organizations.show', org=reuse.organization.id, _external=True), } elif reuse.owner: author = { 'name': reuse.owner.fullname, 'uri': url_for('users.show', user=reuse.owner.id, _external=True), } feed.add(reuse.title, render_template('reuse/feed_item.html', reuse=reuse), content_type='html', author=author, url=url_for('reuses.show', reuse=reuse.id, _external=True), updated=reuse.created_at, published=reuse.created_at) return feed.get_response()
def pre_validate(self, form): if self.data: try: uris.validate(self.data) except uris.ValidationError: raise validators.ValidationError(_('Invalid URL')) return True
def iter_choices(self): localized_choices = [ (value, _(label) if label else '', selected) for value, label, selected in super(SelectField, self).iter_choices() ] for value, label, selected in sorted(localized_choices, key=lambda c: c[1]): yield (value, label, selected)
def notify_new_issue(issue): if isinstance(issue.subject, (Dataset, Reuse)): recipients = owner_recipients(issue) subject = _('Your %(type)s have a new issue', type=issue.subject.verbose_name) mail.send(subject, recipients, 'new_issue', issue=issue) else: log.warning('Unrecognized issue subject type %s', type(issue.subject))
def mark_as_deleted(self): copied_user = copy(self) self.email = '{}@deleted'.format(self.id) self.password = None self.active = False self.first_name = 'DELETED' self.last_name = 'DELETED' self.avatar = None self.avatar_url = None self.website = None self.about = None self.extras = None self.deleted = datetime.now() self.save() for organization in self.organizations: organization.members = [member for member in organization.members if member.user != self] organization.save() for discussion in Discussion.objects(discussion__posted_by=self): for message in discussion.discussion: if message.posted_by == self: message.content = 'DELETED' discussion.save() Follow.objects(follower=self).delete() Follow.objects(following=self).delete() mail.send(_('Account deletion'), copied_user, 'account_deleted')
def get(self, level): '''List each zone for a given level with their datasets count''' level = GeoLevel.objects.get_or_404(id=level) features = [] for zone in GeoZone.objects(level=level.id): # fetch nested levels IDs ids = GeoZone.objects(parents=zone.id).only('id').distinct('id') ids.append(zone.id) # Count datasets in zone nb_datasets = Dataset.objects(spatial__zones__in=ids).count() features.append({ 'id': zone.id, 'type': 'Feature', 'geometry': zone.geom, 'properties': { 'name': _(zone.name), 'code': zone.code, 'level': zone.level, 'datasets': nb_datasets } }) return { 'type': 'FeatureCollection', 'features': features }
def get(self): '''List all known levels''' return [{ 'id': level.id, 'name': _(level.name), 'parents': [p.id for p in level.parents], } for level in GeoLevel.objects]
def notify_badge_added(badge): if isinstance(badge.subject, Organization): recipients = owner_recipients(badge) subject = _('Your %(type)s gain a new badge', type=badge.subject.verbose_name) mail.send(subject, recipients, 'badge_added', badge=badge) else: log.warning('Unrecognized badge subject type %s', type(badge.subject))
def process_formdata(self, valuelist): if valuelist and len(valuelist) == 1 and valuelist[0]: try: self.data = self.model.objects.get(id=clean_oid(valuelist[0], self.model)) except self.model.DoesNotExist: message = _('{0} does not exists').format(self.model.__name__) raise validators.ValidationError(message)
def __call__(self, **kwargs): placeholder = kwargs.pop('placeholder', _(self.label.text)) if placeholder: kwargs['placeholder'] = placeholder required = kwargs.pop('required', self.flags.required) if required is True: kwargs['required'] = required return super(FieldHelper, self).__call__(**kwargs)
def pre_validate(self, form): if self.data: if not current_user.is_authenticated(): raise validators.ValidationError( _('You must be authenticated')) elif not OrganizationPrivatePermission(self.data).can(): raise validators.ValidationError( _("Permission denied for this organization")) # Ensure either owner field or this field value is unset owner_field = form._fields[self.owner_field] if self.raw_data: owner_field.data = None elif getattr(form._obj, self.short_name) and not owner_field.data: pass else: self.data = None return True
def notify_new_issue_comment(issue, **kwargs): if isinstance(issue.subject, (Dataset, Reuse)): comment = kwargs['message'] recipients = owner_recipients(issue) + [m.posted_by for m in issue.discussion] recipients = filter(lambda u: u != comment.posted_by, set(recipients)) subject = _('%(user)s commented your issue', user=comment.posted_by.fullname) mail.send(subject, recipients, 'new_issue_comment', issue=issue, comment=comment) else: log.warning('Unrecognized issue subject type %s', type(issue.subject))
def notify_issue_closed(issue, **kwargs): if isinstance(issue.subject, (Dataset, Reuse)): comment = kwargs['message'] recipients = owner_recipients(issue) + [m.posted_by for m in issue.discussion] recipients = filter(lambda u: u != comment.posted_by, set(recipients)) subject = _('An issue has been closed') mail.send(subject, recipients, 'issue_closed', issue=issue, comment=comment) else: log.warning('Unrecognized issue subject type %s', type(issue.subject))
def notify_new_discussion(discussion): if isinstance(discussion.subject, (Dataset, Reuse)): recipients = owner_recipients(discussion) subject = _('Your %(type)s have a new discussion', type=discussion.subject.verbose_name) mail.send(subject, recipients, 'new_discussion', discussion=discussion) else: log.warning('Unrecognized discussion subject type %s', type(discussion.subject))
def process_formdata(self, valuelist): self.data = [] for name in valuelist: role = datastore.find_role(name) if role is not None: self.data.append(role) else: raise validators.ValidationError( _('The role {role} does not exist').format(role=name))
def notify_new_reuse(reuse): for dataset in reuse.datasets: if dataset.organization: recipients = [m.user for m in dataset.organization.members] elif dataset.owner: recipients = dataset.owner else: recipients = None if recipients: mail.send(_('New reuse'), recipients, 'new_reuse', reuse=reuse, dataset=dataset)
def top_label(self): if not self.zones: return None top = None for zone in self.zones: if not top: top = zone continue if zone.id in top.parents: top = zone return _(top.name)
def notify_issue_closed(issue, **kwargs): if isinstance(issue.subject, (Dataset, Reuse)): message = kwargs['message'] recipients = owner_recipients(issue) + [ m.posted_by for m in issue.discussion] recipients = [u for u in set(recipients) if u != message.posted_by] subject = _('An issue has been closed') mail.send(subject, recipients, 'issue_closed', issue=issue, message=message) else: log.warning('Unrecognized issue subject type %s', type(issue.subject))
def process_formdata(self, valuelist): if valuelist and len(valuelist) == 1 and valuelist[0]: for model in self.models: try: self.data = model.objects.get(id=clean_oid(valuelist[0], model)) except model.DoesNotExist: pass if not self.data: message = _('Model for {0} not found').format(valuelist[0]) raise validators.ValidationError(message)
def pre_validate(self, form): if not self.data: return for tag in self.data: if not tags.MIN_TAG_LENGTH <= len(tag) <= tags.MAX_TAG_LENGTH: message = _( 'Tag "%(tag)s" must be between %(min)d ' 'and %(max)d characters long.', min=tags.MIN_TAG_LENGTH, max=tags.MAX_TAG_LENGTH, tag=tag) raise validators.ValidationError(message)
def labels_for_zone(zone): ''' Extract all known zone labels - main code - keys (postal...) - name translation in every supported languages ''' labels = set([zone.name, zone.code] + zone.keys_values) for lang in current_app.config['LANGUAGES'].keys(): with language(lang): labels.add(_(zone.name)) return list(labels)
class OrganizationSearch(search.ModelSearchAdapter): model = Organization fuzzy = True class Meta: doc_type = 'Organization' name = String(analyzer=search.i18n_analyzer, fields={'raw': String(index='not_analyzed')}) acronym = String(index='not_analyzed') description = String(analyzer=search.i18n_analyzer) badges = String(index='not_analyzed') url = String(index='not_analyzed') created = Date(format='date_hour_minute_second') metrics = search.metrics_mapping_for(Organization) org_suggest = Completion(analyzer=simple, search_analyzer=simple, payloads=True) sorts = { 'name': 'name.raw', 'reuses': 'metrics.reuses', 'datasets': 'metrics.datasets', 'followers': 'metrics.followers', 'views': 'metrics.views', 'created': 'created', 'last_modified': 'last_modified', } facets = { 'reuses': RangeFacet(field='metrics.reuses', ranges=[('none', (None, 1)), ('few', (1, 5)), ('many', (5, None))], labels={ 'none': _('No reuses'), 'few': _('Few reuses'), 'many': _('Many reuses'), }), 'badge': TermsFacet(field='badges', labelizer=organization_badge_labelizer), 'datasets': RangeFacet(field='metrics.datasets', ranges=[('none', (None, 1)), ('few', (1, 5)), ('many', (5, None))], labels={ 'none': _('No datasets'), 'few': _('Few datasets'), 'many': _('Many datasets'), }), 'followers': RangeFacet(field='metrics.followers', ranges=[('none', (None, 1)), ('few', (1, 5)), ('many', (5, None))], labels={ 'none': _('No followers'), 'few': _('Few followers'), 'many': _('Many followers'), }), } boosters = [ search.GaussDecay('metrics.followers', max_followers, decay=lazy('followers_decay')), search.GaussDecay('metrics.reuses', max_reuses, decay=lazy('reuses_decay')), search.GaussDecay('metrics.datasets', max_datasets, decay=lazy('datasets_decay')), ] @classmethod def is_indexable(cls, org): return org.deleted is None @classmethod def serialize(cls, organization): completions = cls.completer_tokenize(organization.name) completions.append(organization.id) if organization.acronym: completions.append(organization.acronym) return { 'name': organization.name, 'acronym': organization.acronym, 'description': organization.description, 'url': organization.url, 'metrics': organization.metrics, 'badges': [badge.kind for badge in organization.badges], 'created': to_iso_datetime(organization.created_at), 'org_suggest': { 'input': completions, 'output': str(organization.id), 'payload': { 'name': organization.name, 'acronym': organization.acronym, 'image_url': organization.logo(40, external=True), 'slug': organization.slug, }, } }
class UserFollowedUser(FollowActivity, Activity): key = 'user:followed' label = _('followed a user') related_to = db.ReferenceField(User) template = 'activity/user.html'
class DiscussionCreateForm(Form): title = fields.StringField(_('Title'), [validators.required()]) comment = fields.StringField(_('Comment'), [validators.required()]) subject = fields.DatasetOrReuseField(_('Subject'), [validators.required()])
class DatasetSearch(ModelSearchAdapter): model = Dataset fuzzy = True exclude_fields = ['spatial.geom', 'spatial.zones.geom'] class Meta: doc_type = 'Dataset' title = String(analyzer=i18n_analyzer, fields={'raw': String(index='not_analyzed')}) description = String(analyzer=i18n_analyzer) license = String(index='not_analyzed') frequency = String(index='not_analyzed') organization = String(index='not_analyzed') owner = String(index='not_analyzed') tags = String(index='not_analyzed', fields={'i18n': String(index='not_analyzed')}) badges = String(index='not_analyzed') tag_suggest = Completion(analyzer=simple, search_analyzer=simple, payloads=False) resources = Object( properties={ 'title': String(), 'description': String(), 'format': String(index='not_analyzed') }) format_suggest = Completion(analyzer=simple, search_analyzer=simple, payloads=False) dataset_suggest = Completion(analyzer=simple, search_analyzer=simple, payloads=True) created = Date(format='date_hour_minute_second') last_modified = Date(format='date_hour_minute_second') metrics = metrics_mapping_for(Dataset) featured = Boolean() temporal_coverage = Nested(multi=False, properties={ 'start': Long(), 'end': Long() }) temporal_weight = Long(), geozones = Object( properties={ 'id': String(index='not_analyzed'), 'name': String(index='not_analyzed'), 'keys': String(index='not_analyzed') }) granularity = String(index='not_analyzed') spatial_weight = Long() from_certified = Boolean() fields = ( 'geozones.keys^9', 'geozones.name^9', 'acronym^7', 'title^6', 'tags.i18n^3', 'description', ) sorts = { 'title': 'title.raw', 'created': 'created', 'last_modified': 'last_modified', 'reuses': 'metrics.reuses', 'followers': 'metrics.followers', 'views': 'metrics.views', } facets = { 'tag': TermsFacet(field='tags'), 'badge': TermsFacet(field='badges', labelizer=dataset_badge_labelizer), 'organization': ModelTermsFacet(field='organization', model=Organization), 'owner': ModelTermsFacet(field='owner', model=User), 'license': ModelTermsFacet(field='license', model=License), 'geozone': ModelTermsFacet(field='geozones.id', model=GeoZone, labelizer=zone_labelizer), 'granularity': TermsFacet(field='granularity', labelizer=granularity_labelizer), 'format': TermsFacet(field='resources.format'), 'resource_type': TermsFacet(field='resources.type', labelizer=resource_type_labelizer), 'reuses': RangeFacet(field='metrics.reuses', ranges=[('none', (None, 1)), ('few', (1, 5)), ('quite', (5, 10)), ('many', (10, None))], labels={ 'none': _('Never reused'), 'few': _('Little reused'), 'quite': _('Quite reused'), 'many': _('Heavily reused'), }), 'temporal_coverage': TemporalCoverageFacet(field='temporal_coverage'), 'featured': BoolFacet(field='featured'), } boosters = [ BoolBooster('featured', 1.5), BoolBooster('from_certified', 1.2), ValueFactor('spatial_weight', missing=1), ValueFactor('temporal_weight', missing=1), GaussDecay('metrics.reuses', max_reuses, decay=0.1), GaussDecay('metrics.followers', max_followers, max_followers, decay=0.1), ] @classmethod def is_indexable(cls, dataset): return (dataset.deleted is None and len(dataset.resources) > 0 and not dataset.private) @classmethod def get_suggest_weight(cls, temporal_weight, spatial_weight, featured): '''Compute the suggest part of the indexation payload''' featured_weight = 1 if not featured else FEATURED_WEIGHT return int(temporal_weight * spatial_weight * featured_weight * 10) @classmethod def serialize(cls, dataset): organization = None owner = None image_url = None spatial_weight = DEFAULT_SPATIAL_WEIGHT temporal_weight = DEFAULT_TEMPORAL_WEIGHT if dataset.organization: organization = Organization.objects( id=dataset.organization.id).first() image_url = organization.logo(40, external=True) elif dataset.owner: owner = User.objects(id=dataset.owner.id).first() image_url = owner.avatar(40, external=True) certified = organization and organization.certified document = { 'title': dataset.title, 'description': dataset.description, 'license': getattr(dataset.license, 'id', None), 'tags': dataset.tags, 'badges': [badge.kind for badge in dataset.badges], 'tag_suggest': dataset.tags, 'resources': [{ 'title': r.title, 'description': r.description, 'format': r.format, 'type': r.type, } for r in dataset.resources], 'format_suggest': [r.format.lower() for r in dataset.resources if r.format], 'frequency': dataset.frequency, 'organization': str(organization.id) if organization else None, 'owner': str(owner.id) if owner else None, 'dataset_suggest': { 'input': cls.completer_tokenize(dataset.title) + [str(dataset.id)], 'output': dataset.title, 'payload': { 'id': str(dataset.id), 'slug': dataset.slug, 'acronym': dataset.acronym, 'image_url': image_url, }, }, 'created': dataset.created_at.strftime('%Y-%m-%dT%H:%M:%S'), 'last_modified': dataset.last_modified.strftime('%Y-%m-%dT%H:%M:%S'), 'metrics': dataset.metrics, 'featured': dataset.featured, 'from_certified': certified, } if (dataset.temporal_coverage is not None and dataset.temporal_coverage.start and dataset.temporal_coverage.end): start = dataset.temporal_coverage.start.toordinal() end = dataset.temporal_coverage.end.toordinal() temporal_weight = min((end - start) / 365, MAX_TEMPORAL_WEIGHT) document.update({ 'temporal_coverage': { 'start': start, 'end': end }, 'temporal_weight': temporal_weight, }) if dataset.spatial is not None: # Index precise zone labels and parents zone identifiers # to allow fast filtering. zone_ids = [z.id for z in dataset.spatial.zones] zones = GeoZone.objects(id__in=zone_ids).exclude('geom') parents = set() geozones = [] coverage_level = ADMIN_LEVEL_MAX for zone in zones: geozones.append({ 'id': zone.id, 'name': zone.name, 'keys': zone.keys_values }) parents |= set(zone.parents) coverage_level = min(coverage_level, admin_levels[zone.level]) geozones.extend([{'id': p} for p in parents]) spatial_weight = ADMIN_LEVEL_MAX / coverage_level document.update({ 'geozones': geozones, 'granularity': dataset.spatial.granularity, 'spatial_weight': spatial_weight, }) document['dataset_suggest']['weight'] = cls.get_suggest_weight( temporal_weight, spatial_weight, dataset.featured) if dataset.acronym: document['dataset_suggest']['input'].append(dataset.acronym) return document
class UserFollowedOrganization(FollowActivity, OrgRelatedActivity, Activity): key = 'organization:followed' label = _('followed an organization')
class OrganizationExtraForm(Form): key = fields.StringField(_('Key'), [validators.required()]) value = fields.StringField(_('Value'), [validators.required()]) old_key = fields.StringField(_('Old key'))
class UserFollowedDataset(FollowActivity, DatasetRelatedActivity, Activity): key = 'dataset:followed' label = _('followed a dataset')
class UserDiscussedReuse(DiscussActivity, ReuseRelatedActivity, Activity): key = 'reuse:discussed' label = _('discussed a reuse')
class UserDiscussedDataset(DiscussActivity, DatasetRelatedActivity, Activity): key = 'dataset:discussed' label = _('discussed a dataset')
def payload_name(payload): '''extract payload name and localize it''' name = payload['name'] return _(name) # Avoid dict quotes in gettext
class MembershipRefuseForm(Form): comment = fields.StringField(_('Comment'), [validators.required()])
class CommunityResourceForm(BaseResourceForm): model_class = CommunityResource dataset = fields.DatasetField(_('Related dataset')) owner = fields.CurrentUserField() organization = fields.PublishAsField(_('Publish as'))
class DiscussionCommentForm(Form): comment = fields.StringField(_('Comment'), [validators.required()]) close = fields.BooleanField(default=False)
class DatasetForm(ModelForm): model_class = Dataset title = fields.StringField(_('Title'), [validators.required()]) description = fields.MarkdownField( _('Description'), [validators.required()], description=_('The details about the dataset ' '(collection process, specifics...).')) license = fields.ModelSelectField(_('License'), model=License, allow_blank=True) frequency = fields.SelectField( _('Update frequency'), choices=UPDATE_FREQUENCIES.items(), default=DEFAULT_FREQUENCY, validators=[validators.optional()], preprocessors=[map_legacy_frequencies], description=_('The frequency at which data are updated.')) frequency_date = fields.DateTimeField(_('Expected frequency date')) temporal_coverage = fields.DateRangeField( _('Temporal coverage'), description=_('The period covered by the data')) spatial = SpatialCoverageField( _('Spatial coverage'), description=_('The geographical area covered by the data.')) tags = fields.TagField(_('Tags'), description=_('Some taxonomy keywords')) private = fields.BooleanField( _('Private'), description=_('Restrict the dataset visibility to you or ' 'your organization only.')) owner = fields.CurrentUserField() organization = fields.PublishAsField(_('Publish as')) extras = fields.ExtrasField(extras=Dataset.extras) resources = fields.NestedModelList(ResourceForm)
def __html__(self): """In use within the admin.""" return '{name} <i>({code})</i>'.format(name=_(self.name), code=self.code)
class HarvestSourceValidationForm(Form): state = fields.SelectField(choices=VALIDATION_STATES.items()) comment = fields.StringField( _('Comment'), [validators.RequiredIfVal('state', VALIDATION_REFUSED)])
class BaseResourceForm(ModelForm): title = fields.StringField(_('Title'), [validators.required()]) description = fields.MarkdownField(_('Description')) filetype = fields.RadioField( _('File type'), [validators.required(), enforce_filetype_file], choices=RESOURCE_FILETYPES.items(), default='file', description=_('Whether the resource is an uploaded file, ' 'a remote file or an API')) type = fields.RadioField( _('Type'), [validators.required()], choices=RESOURCE_TYPES.items(), default='other', description=_('Resource type (documentation, API...)')) url = fields.UploadableURLField(_('URL'), [validators.required()], storage=resources) format = fields.StringField( _('Format'), filters=[normalize_format], ) checksum = fields.FormField(ChecksumForm) mime = fields.StringField(_('Mime type'), description=_( 'The mime type associated to the extension. ' '(ex: text/plain)')) filesize = fields.IntegerField(_('Size'), [validators.optional()], description=_('The file size in bytes')) published = fields.DateTimeField( _('Publication date'), description=_('The publication date of the resource'))
def get_spatial_granularities(lang): with language(lang): return [(l.id, _(l.name)) for l in GeoLevel.objects ] + [(id, label.value) for id, label in BASE_GRANULARITIES]
def notify_new_member(org, member): subject = _('You are now a member of the organization "%(org)s"', org=org) mail.send(subject, member.user, 'new_member', org=org)
class UserCreatedDataset(DatasetRelatedActivity, Activity): key = 'dataset:created' icon = 'fa fa-plus' badge_type = 'success' label = _('created a dataset')
def notify_membership_request(org, request): recipients = [m.user for m in org.by_role('admin')] mail.send( _('New membership request'), recipients, 'membership_request', org=org, request=request)
class UserUpdatedDataset(DatasetRelatedActivity, Activity): key = 'dataset:updated' icon = 'fa fa-pencil' label = _('updated a dataset')
class Organization(WithMetrics, BadgeMixin, db.Datetimed, db.Document): name = db.StringField(required=True) acronym = db.StringField(max_length=128) slug = db.SlugField(max_length=255, required=True, populate_from='name', update=True, follow=True) description = db.StringField(required=True) url = db.StringField() image_url = db.StringField() logo = db.ImageField(fs=avatars, basename=default_image_basename, max_size=LOGO_MAX_SIZE, thumbnails=LOGO_SIZES) members = db.ListField(db.EmbeddedDocumentField(Member)) teams = db.ListField(db.EmbeddedDocumentField(Team)) requests = db.ListField(db.EmbeddedDocumentField(MembershipRequest)) ext = db.MapField(db.GenericEmbeddedDocumentField()) zone = db.StringField() extras = db.ExtrasField() deleted = db.DateTimeField() meta = { 'indexes': ['-created_at', 'slug'], 'ordering': ['-created_at'], 'queryset_class': OrganizationQuerySet, } def __unicode__(self): return self.name or '' __badges__ = { PUBLIC_SERVICE: _('Public Service'), CERTIFIED: _('Certified'), } before_save = Signal() after_save = Signal() on_create = Signal() on_update = Signal() before_delete = Signal() after_delete = Signal() @classmethod def pre_save(cls, sender, document, **kwargs): cls.before_save.send(document) @classmethod def post_save(cls, sender, document, **kwargs): cls.after_save.send(document) if kwargs.get('created'): cls.on_create.send(document) else: cls.on_update.send(document) def url_for(self, *args, **kwargs): return url_for('organizations.show', org=self, *args, **kwargs) display_url = property(url_for) @property def external_url(self): return self.url_for(_external=True) @property def pending_requests(self): return [r for r in self.requests if r.status == 'pending'] @property def refused_requests(self): return [r for r in self.requests if r.status == 'refused'] @property def accepted_requests(self): return [r for r in self.requests if r.status == 'accepted'] @property def certified(self): return any(b.kind == CERTIFIED for b in self.badges) @property def public_service(self): is_public_service = any(b.kind == PUBLIC_SERVICE for b in self.badges) return self.certified and is_public_service def member(self, user): for member in self.members: if member.user == user: return member return None def is_member(self, user): return self.member(user) is not None def is_admin(self, user): member = self.member(user) return member is not None and member.role == 'admin' def pending_request(self, user): for request in self.requests: if request.user == user and request.status == 'pending': return request return None @classmethod def get(cls, id_or_slug): obj = cls.objects(slug=id_or_slug).first() return obj or cls.objects.get_or_404(id=id_or_slug) def by_role(self, role): return filter(lambda m: m.role == role, self.members) def check_availability(self): from udata.models import Dataset # Circular imports. # Performances: only check the first 20 datasets for now. return chain(*[ dataset.check_availability() for dataset in Dataset.objects(organization=self).visible()[:20] ]) @cached_property def json_ld(self): type_ = 'GovernmentOrganization' if self.public_service \ else 'Organization' result = { '@context': 'http://schema.org', '@type': type_, 'alternateName': self.slug, 'url': url_for('organizations.show', org=self, _external=True), 'name': self.name, } if self.description: result['description'] = mdstrip(self.description) logo = self.logo(external=True) if logo: result['logo'] = logo return result
class UserDeletedDataset(DatasetRelatedActivity, Activity): key = 'dataset:deleted' icon = 'fa fa-remove' badge_type = 'error' label = _('deleted a dataset')
from blinker import Signal from flask import url_for from mongoengine.signals import pre_save, post_save from werkzeug import cached_property from udata.core.storages import avatars, default_image_basename from udata.frontend.markdown import mdstrip from udata.models import db, BadgeMixin, WithMetrics from udata.i18n import lazy_gettext as _ __all__ = ('Organization', 'Team', 'Member', 'MembershipRequest', 'ORG_ROLES', 'MEMBERSHIP_STATUS', 'PUBLIC_SERVICE', 'CERTIFIED') ORG_ROLES = { 'admin': _('Administrator'), 'editor': _('Editor'), } DEFAULT_ROLE = 'editor' MEMBERSHIP_STATUS = { 'pending': _('Pending'), 'accepted': _('Accepted'), 'refused': _('Refused'), } LOGO_MAX_SIZE = 500 LOGO_SIZES = [100, 60, 25] PUBLIC_SERVICE = 'public-service' CERTIFIED = 'certified'
class FollowersMetric(SiteMetric): name = 'followers' display_name = _('Followers') def get_value(self): return Follow.objects(until=None).count()
class DatasetsMetric(SiteMetric): name = 'datasets' display_name = _('Datasets') def get_value(self): return Dataset.objects.visible().count()
def source_tooltip_callback(attrs, new=False): """ Add a `data-tooltip` attribute with `Source` content for embeds. """ attrs[(None, 'data-tooltip')] = _('Source') return attrs
class UserFollowedReuse(FollowActivity, ReuseRelatedActivity, Activity): key = 'reuse:followed' label = _('followed a reuse')
class MembershipRequestForm(UserModelForm): model_class = MembershipRequest user_field = 'user' comment = fields.StringField(_('Comment'), [validators.required()])