diff --git a/app/main/routes.py b/app/main/routes.py index 1e7665a3..664e5e94 100644 --- a/app/main/routes.py +++ b/app/main/routes.py @@ -1,5 +1,5 @@ from flask import flash, redirect, render_template, url_for -from flask_login import login_required, login_user +from flask_login import current_user, login_required, login_user from app.auth.forms import LoginForm from app.models import User from . import bp @@ -27,7 +27,11 @@ def faq(): @bp.route('/dashboard') @login_required def dashboard(): - return render_template('main/dashboard.html.j2', title='Dashboard') + users = [ + u.to_json_serializeable(filter_by_privacy_settings=True, include_avatar_relationship=True) for u + in User.query.filter(User.is_public == True, User.id != current_user.id).all() + ] + return render_template('main/dashboard.html.j2', title='Dashboard', users=users) @bp.route('/user_manual') diff --git a/app/models.py b/app/models.py index d28331a0..fbec78fa 100644 --- a/app/models.py +++ b/app/models.py @@ -254,7 +254,7 @@ class Avatar(HashidMixin, FileMixin, db.Model): # Primary key id = db.Column(db.Integer, primary_key=True) # Foreign keys - user_id = db.Column(db.Integer, db.ForeignKey('users.id')) + user_id = db.Column(db.Integer, db.ForeignKey('users.id'), unique=True) @property def path(self): @@ -266,6 +266,12 @@ class Avatar(HashidMixin, FileMixin, db.Model): except OSError as e: current_app.logger.error(e) db.session.delete(self) + + def to_json_serializeable(self, backrefs=False, relationships=False): + json_serializeable = { + 'id': self.id + } + return json_serializeable class User(HashidMixin, UserMixin, db.Model): __tablename__ = 'users' @@ -273,7 +279,6 @@ class User(HashidMixin, UserMixin, db.Model): id = db.Column(db.Integer, primary_key=True) # Foreign keys role_id = db.Column(db.Integer, db.ForeignKey('roles.id')) - # privacy_id = db.Column(db.Integer, db.ForeignKey('privacies.id')) # Fields email = db.Column(db.String(254), index=True, unique=True) username = db.Column(db.String(64), index=True, unique=True) @@ -533,7 +538,16 @@ class User(HashidMixin, UserMixin, db.Model): self.profile_privacy_settings = 0 #endregion Profile Privacy settings - def to_json_serializeable(self, backrefs=False, relationships=False): + def to_json_serializeable( + self, backrefs=False, + relationships=False, + filter_by_privacy_settings=False, + include_corpus_relationships=False, + include_job_relationships=False, + include_tesseract_ocr_pipeline_model_relationships=False, + include_spacy_nlp_pipeline_model_relationships=False, + include_avatar_relationship=False + ): json_serializeable = { 'id': self.hashid, 'confirmed': self.confirmed, @@ -555,28 +569,43 @@ class User(HashidMixin, UserMixin, db.Model): 'show_email': self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_EMAIL), 'show_last_seen': self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_LAST_SEEN), 'show_member_since': self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_MEMBER_SINCE) - } if backrefs: json_serializeable['role'] = \ self.role.to_json_serializeable(backrefs=True) - if relationships: + if relationships or include_corpus_relationships: json_serializeable['corpora'] = { x.hashid: x.to_json_serializeable(relationships=True) for x in self.corpora } + if relationships or include_job_relationships: json_serializeable['jobs'] = { x.hashid: x.to_json_serializeable(relationships=True) for x in self.jobs } + if relationships or include_tesseract_ocr_pipeline_model_relationships: json_serializeable['tesseract_ocr_pipeline_models'] = { x.hashid: x.to_json_serializeable(relationships=True) for x in self.tesseract_ocr_pipeline_models } + if relationships or include_spacy_nlp_pipeline_model_relationships: json_serializeable['spacy_nlp_pipeline_models'] = { x.hashid: x.to_json_serializeable(relationships=True) for x in self.spacy_nlp_pipeline_models } + if relationships or include_avatar_relationship: + json_serializeable['avatar'] = ( + None if self.avatar is None + else self.avatar.to_json_serializeable(relationships=True) + ) + + if filter_by_privacy_settings: + if not self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_EMAIL): + json_serializeable.pop('email') + if not self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_LAST_SEEN): + json_serializeable.pop('last_seen') + if not self.has_profile_privacy_setting(ProfilePrivacySettings.SHOW_MEMBER_SINCE): + json_serializeable.pop('member_since') return json_serializeable class TesseractOCRPipelineModel(FileMixin, HashidMixin, db.Model): diff --git a/app/profile/routes.py b/app/profile/routes.py index b4711aa5..8c1f8e81 100644 --- a/app/profile/routes.py +++ b/app/profile/routes.py @@ -54,7 +54,6 @@ def delete_avatar(avatar_id, user_id): def _delete_avatar(app, avatar_id): with app.app_context(): avatar_file = Avatar.query.get(avatar_id) - print(avatar_file) avatar_file.delete() db.session.commit() thread = Thread( diff --git a/app/static/js/RessourceLists/PublicUserList.js b/app/static/js/RessourceLists/PublicUserList.js new file mode 100644 index 00000000..4597ed3e --- /dev/null +++ b/app/static/js/RessourceLists/PublicUserList.js @@ -0,0 +1,79 @@ +class PublicUserList extends RessourceList { + static autoInit() { + for (let publicUserListElement of document.querySelectorAll('.public-user-list:not(.no-autoinit)')) { + new PublicUserList(publicUserListElement); + } + } + + static options = { + initialHtmlGenerator: (id) => { + console.log(id); + return ` +
+ search + + +
+ + + + + + + + + +
Username
+ + `.trim(); + }, + item: ` + + user-image + + + send + + + `.trim(), + ressourceMapper: (user) => { + return { + 'id': user.id, + 'member-since': user.member_since, + 'avatar': `/static/images/user_avatar.png`, + 'username': user.username + }; + }, + sortArgs: ['member-since', {order: 'desc'}], + valueNames: [ + {data: ['id']}, + {data: ['member-since']}, + {name: 'avatar', attr: 'src'}, + 'username' + ] + }; + + constructor(listElement, options = {}) { + super(listElement, {...PublicUserList.options, ...options}); + } + + init(users) { + super._init(Object.values(users)); + } + + onClick(event) { + let actionButtonElement = event.target.closest('.action-button'); + let action = actionButtonElement === null ? 'view' : actionButtonElement.dataset.action; + let publicUserElement = event.target.closest('tr'); + let publicUserId = publicUserElement.dataset.id; + switch (action) { + case 'view': { + window.location.href = `/profile/${publicUserId}`; + break; + } + default: { + break; + } + } + } +} diff --git a/app/static/js/RessourceLists/RessourceList.js b/app/static/js/RessourceLists/RessourceList.js index 5af7a231..097c3763 100644 --- a/app/static/js/RessourceLists/RessourceList.js +++ b/app/static/js/RessourceLists/RessourceList.js @@ -11,6 +11,7 @@ class RessourceList { JobInputList.autoInit(); JobResultList.autoInit(); PublicCorporaList.autoInit(); + PublicUserList.autoInit(); SpaCyNLPPipelineModelList.autoInit(); TesseractOCRPipelineModelList.autoInit(); UserList.autoInit(); diff --git a/app/templates/_scripts.html.j2 b/app/templates/_scripts.html.j2 index 8ff90c33..ced76bab 100644 --- a/app/templates/_scripts.html.j2 +++ b/app/templates/_scripts.html.j2 @@ -25,6 +25,7 @@ 'js/RessourceLists/JobInputList.js', 'js/RessourceLists/JobResultList.js', 'js/RessourceLists/PublicCorporaList.js', + 'js/RessourceLists/PublicUserList.js', 'js/RessourceLists/SpacyNLPPipelineModelList.js', 'js/RessourceLists/TesseractOCRPipelineModelList.js', 'js/RessourceLists/UserList.js' diff --git a/app/templates/admin/users.html.j2 b/app/templates/admin/users.html.j2 index 75254b0e..55c5dc2d 100644 --- a/app/templates/admin/users.html.j2 +++ b/app/templates/admin/users.html.j2 @@ -22,11 +22,7 @@ {% block scripts %} {{ super() }} {% endblock scripts %} diff --git a/app/templates/main/dashboard.html.j2 b/app/templates/main/dashboard.html.j2 index e0354ea6..eb504cb3 100644 --- a/app/templates/main/dashboard.html.j2 +++ b/app/templates/main/dashboard.html.j2 @@ -49,6 +49,7 @@
Other users and groups

Find other users and follow them to see their corpora and groups.

+
@@ -115,3 +116,11 @@ {% endblock modals %} + +{% block scripts %} +{{ super() }} + +{% endblock scripts %}