From f019f524d7a8afe267febd588ddfda0c276e47e1 Mon Sep 17 00:00:00 2001 From: Vivek Anand Date: Aug 31 2017 13:50:47 +0000 Subject: Star a project Signed-off-by: Vivek Anand --- diff --git a/alembic/versions/c34f4b09ef18_star_a_project.py b/alembic/versions/c34f4b09ef18_star_a_project.py new file mode 100644 index 0000000..dcaecd1 --- /dev/null +++ b/alembic/versions/c34f4b09ef18_star_a_project.py @@ -0,0 +1,51 @@ +"""star_a_project + +Revision ID: c34f4b09ef18 +Revises: 27a79ff0fb41 +Create Date: 2017-07-07 00:08:18.257075 + +""" + +# revision identifiers, used by Alembic. +revision = 'c34f4b09ef18' +down_revision = '27a79ff0fb41' + +from alembic import op +import sqlalchemy as sa + + +def upgrade(): + ''' Add a new table to store data about who starred which project ''' + op.create_table( + 'stargazers', + sa.MetaData(), + sa.Column('id', sa.Integer, primary_key=True), + sa.Column( + 'project_id', + sa.Integer, + sa.ForeignKey( + 'projects.id', onupdate='CASCADE', ondelete='CASCADE'), + nullable=False + ), + sa.Column( + 'user_id', + sa.Integer, + sa.ForeignKey('users.id', onupdate='CASCADE', ondelete='CASCADE'), + nullable=False, + ) + ) + + op.create_unique_constraint( + constraint_name='stargazers_project_id_user_id_key', + table_name='stargazers', + columns=['project_id', 'user_id'] + ) + + +def downgrade(): + ''' Remove the stargazers table from the database ''' + op.drop_constraint( + constraint_name='stargazers_project_id_user_id_key', + table_name='stargazers' + ) + op.drop_table('stargazers') diff --git a/pagure/__init__.py b/pagure/__init__.py index 0da5514..c58df7f 100644 --- a/pagure/__init__.py +++ b/pagure/__init__.py @@ -500,6 +500,9 @@ def set_variables(): flask.g.repo_forked = pagure.get_authorized_project( SESSION, repo, user=flask.g.fas_user.username, namespace=namespace) + flask.g.repo_starred = pagure.lib.has_starred( + SESSION, flask.g.repo, user=flask.g.fas_user.username, + ) if not flask.g.repo \ and APP.config.get('OLD_VIEW_COMMIT_ENABLED', False) \ diff --git a/pagure/lib/__init__.py b/pagure/lib/__init__.py index db46b11..8e3d347 100644 --- a/pagure/lib/__init__.py +++ b/pagure/lib/__init__.py @@ -4359,3 +4359,101 @@ def get_pagination_metadata(flask_request, page, per_page, total): 'first': first_page, 'last': last_page } + + +def update_star_project(session, repo, star, user): + ''' Unset or set the star status depending on the star value. + + :arg session: the session to use to connect to the database. + :arg repo: a model.Project object representing the project to star/unstar + :arg user: string representing the user + :return: string containg 'starred' or 'unstarred' + ''' + + if not all([repo, user, star]): + return + user_obj = get_user(session, user) + if star == '1': + msg = _star_project( + session, + repo=repo, + user=user_obj, + ) + return msg + msg = _unstar_project( + session, + repo=repo, + user=user_obj, + ) + return msg + + +def _star_project(session, repo, user): + ''' Star a project + + :arg session: Session object to connect to db with + :arg repo: model.Project object representing the repo to star + :arg user: model.User object who is starring this repo + ''' + + if not all([repo, user]): + return + stargazer_obj = model.Star( + project_id=repo.id, + user_id=user.id, + ) + session.add(stargazer_obj) + return 'You liked this project!' + + +def _unstar_project(session, repo, user): + ''' Unstar a project + :arg session: Session object to connect to db with + :arg repo: model.Project object representing the repo to star + :arg user: model.User object who is unstarring this repo + ''' + + if not all([repo, user]): + return + # First find the stargazer_obj object + stargazer_obj = _get_stargazer_obj(session, repo, user) + session.delete(stargazer_obj) + return 'You didn\'t like this project :(' + + +def _get_stargazer_obj(session, repo, user): + ''' Query the db to find stargazer object with given repo and user + :arg session: Session object to connect to db with + :arg repo: model.Project object representing the repo to star + :arg user: model.User object who is starring this repo + ''' + + if not all([repo, user]): + return + stargazer_obj = session.query( + model.Star, + ).filter( + model.Star.project_id == repo.id, + ).filter( + model.Star.user_id == user.id, + ) + + return stargazer_obj.first() + + +def has_starred(session, repo, user): + ''' Check if a given user has starred a particular project + + :arg session: The session object to query the db with + :arg repo: model.Project object for which the star is checked + :arg user: The username of the user in question + :type user: str + ''' + + if not all([repo, user]): + return + user_obj = get_user(session, user) + stargazer_obj = _get_stargazer_obj(session, repo, user_obj) + if isinstance(stargazer_obj, model.Star): + return True + return False diff --git a/pagure/lib/model.py b/pagure/lib/model.py index 15aacc0..15bfce2 100644 --- a/pagure/lib/model.py +++ b/pagure/lib/model.py @@ -2097,6 +2097,43 @@ class ProjectGroup(BASE): __table_args__ = (sa.UniqueConstraint('project_id', 'group_id'),) +class Star(BASE): + """ Stores users association with the all the projects which + they have starred + + Table -- star + """ + + __tablename__ = 'stargazers' + __table_args__ = ( + sa.UniqueConstraint('project_id', 'user_id'), + ) + + id = sa.Column(sa.Integer, primary_key=True) + project_id = sa.Column( + sa.Integer, + sa.ForeignKey('projects.id', onupdate='CASCADE'), + nullable=False) + user_id = sa.Column( + sa.Integer, + sa.ForeignKey('users.id', onupdate='CASCADE'), + nullable=False, + index=True + ) + user = relation( + 'User', foreign_keys=[user_id], remote_side=[User.id], + backref=backref( + 'stars', cascade="delete, delete-orphan" + ), + ) + project = relation( + 'Project', foreign_keys=[project_id], remote_side=[Project.id], + backref=backref( + 'stargazers', cascade="delete, delete-orphan", + ), + ) + + class Watcher(BASE): """ Stores the user of a projects. diff --git a/pagure/templates/master.html b/pagure/templates/master.html index 24ea2cd..085c9a2 100644 --- a/pagure/templates/master.html +++ b/pagure/templates/master.html @@ -75,6 +75,11 @@ url_for('view_user_requests', username=g.fas_user.username) }}">My Pull Requests + My Stars + + Log Out diff --git a/pagure/templates/repo_master.html b/pagure/templates/repo_master.html index e109c7a..80c5e07 100644 --- a/pagure/templates/repo_master.html +++ b/pagure/templates/repo_master.html @@ -50,6 +50,46 @@ class="btn btn-success btn-sm">New Issue {% endif %}
+ {% if not g.repo_starred %} +
+ {{ forkbuttonform.csrf_token }} +
+ + {{repo.stargazers|length}} + {% else %} +
+ {{ forkbuttonform.csrf_token }} +
+ + {{repo.stargazers|length}} + {% endif %} + {% if not repo.is_fork %} {% if g.repo_forked %} +
+

+ Starred by {{ users | length}} users +

+
+ + + + + + + + {% for user in users %} + + + + {% endfor %} + +
Stargazers of {{ repo.fullname }}
+ + {{user.user}} +
+
+
+
+
+{% endblock %} diff --git a/pagure/templates/user_stars.html b/pagure/templates/user_stars.html new file mode 100644 index 0000000..aec3a29 --- /dev/null +++ b/pagure/templates/user_stars.html @@ -0,0 +1,50 @@ +{% extends "master.html" %} + +{% block title %}My Starred Projects{% endblock %} + +{% block content %} +
+
+

+ {{ repos | length }} Projects Starred by You +

+
+ + + + + + + + {% for repo in repos %} + + + + {% endfor %} + +
My Starred Projects
+ {% if repo.avatar_email %} + + {{repo.fullname}} +
+ {% else %} + +
+ {% endif %} +
+
+
+
+{% endblock %} diff --git a/pagure/ui/app.py b/pagure/ui/app.py index 9e4f640..fae7d19 100644 --- a/pagure/ui/app.py +++ b/pagure/ui/app.py @@ -432,6 +432,26 @@ def view_user_issues(username): ) +@APP.route('/user//stars/') +@APP.route('/user//stars') +def view_user_stars(username): + """ + Shows the starred projects of the specified user. + + :param username: The username whose stars we have to retrieve + :type username: str + """ + + user = _get_user(username=username) + + return flask.render_template( + 'user_stars.html', + username=username, + user=user, + repos=[star.project for star in user.stars], + ) + + @APP.route('/new/', methods=('GET', 'POST')) @APP.route('/new', methods=('GET', 'POST')) @login_required diff --git a/pagure/ui/repo.py b/pagure/ui/repo.py index d09d3c3..e2d3acd 100644 --- a/pagure/ui/repo.py +++ b/pagure/ui/repo.py @@ -2263,6 +2263,53 @@ def view_project_activity(repo, namespace=None): ) +@APP.route('//stargazers/') +@APP.route('/fork///stargazers/') +@APP.route('///stargazers/') +@APP.route('/fork////stargazers/') +def view_stargazers(repo, username=None, namespace=None): + ''' View all the users who have starred the project ''' + + stargazers = flask.g.repo.stargazers + users = [star.user for star in stargazers] + return flask.render_template( + 'repo_stargazers.html', repo=flask.g.repo, users=users) + + +@APP.route('//star/', methods=["POST"]) +@APP.route('/fork///star/', methods=["POST"]) +@APP.route('///star/', methods=["POST"]) +@APP.route('/fork////star/', methods=["POST"]) +@login_required +def star_project(repo, star, username=None, namespace=None): + ''' Star a project ''' + + return_point = flask.url_for('index') + if pagure.is_safe_url(flask.request.referrer): + return_point = flask.request.referrer + + form = pagure.forms.ConfirmationForm() + if not form.validate_on_submit(): + flask.abort(400) + + if star not in ['0', '1']: + flask.abort(400) + + try: + msg = pagure.lib.update_star_project( + SESSION, + user=flask.g.fas_user.username, + repo=flask.g.repo, + star=star, + ) + SESSION.commit() + flask.flash(msg) + except SQLAlchemyError: + flask.flash('Could not star the project') + + return flask.redirect(return_point) + + @APP.route('//watch/settings/', methods=['POST']) @APP.route('///watch/settings/', methods=['POST']) @APP.route(