# -*- coding: utf-8 -*-
"""
(c) 2015-2016 - Copyright Red Hat Inc
Authors:
Pierre-Yves Chibon <pingou@pingoured.fr>
"""
from __future__ import unicode_literals, absolute_import
import collections
import datetime
import arrow
import flask
import six
import pagure
import pagure.exceptions
import pagure.lib.query
from pagure.api import API, api_method, APIERROR, get_page, get_per_page
from pagure.utils import is_true, validate_date, validate_date_range
def _get_user(username):
""" Check user is valid or not
"""
try:
return pagure.lib.query.get_user(flask.g.session, username)
except pagure.exceptions.PagureException:
raise pagure.exceptions.APIError(404, error_code=APIERROR.ENOUSER)
@API.route("/user/<username>")
@api_method
def api_view_user(username):
"""
User information
----------------
Use this endpoint to retrieve information about a specific user.
::
GET /api/0/user/<username>
::
GET /api/0/user/ralph
Parameters
^^^^^^^^^^
+---------------+----------+---------------+--------------------------+
| Key | Type | Optionality | Description |
+===============+==========+===============+==========================+
| ``repopage`` | int | Optional | | Specifies which |
| | | | page of the projects |
| | | | to return |
| | | | (defaults to: 1) |
+---------------+----------+---------------+--------------------------+
| ``forkpage`` | int | Optional | | Specifies which |
| | | | page of the forks |
| | | | to return |
| | | | (defaults to: 1) |
+---------------+----------+---------------+--------------------------+
| ``per_page`` | int | Optional | | The number of items |
| | | | to return per page. |
| | | | The maximum is 100. |
+---------------+----------+---------------+--------------------------+
Sample response
^^^^^^^^^^^^^^^
::
{
"forks": [],
"repos": [
{
"custom_keys": [],
"description": "",
"parent": null,
"tags": [],
"namespace": None,
"priorities": {},
"close_status": [
"Invalid",
"Insufficient data",
"Fixed",
"Duplicated"
],
"milestones": {},
"user": {
"fullname": "ralph",
"name": "ralph"
},
"date_created": "1426595173",
"id": 5,
"name": "pagure"
}
],
"repos_pagination": {
"first": "http://localhost:5000/api/0/user/ralph?per_page=1&repopage=1",
"last": "http://localhost:5000/api/0/user/ralph?per_page=1&repopage=123",
"next": "http://localhost:5000/api/0/user/ralph?per_page=1&repopage=2",
"pages": 123,
"per_page": 1,
"prev": null,
"repopage": 1
},
"user": {
"fullname": "ralph",
"name": "ralph",
"avatar_url": "https://seccdn.libravatar.org/avatar/5dac3?s=16&d=retro"
}
}
""" # noqa
httpcode = 200
output = {}
user = _get_user(username=username)
per_page = get_per_page()
repopage = flask.request.args.get("repopage", 1)
try:
repopage = int(repopage)
except ValueError:
repopage = 1
forkpage = flask.request.args.get("forkpage", 1)
try:
forkpage = int(forkpage)
except ValueError:
forkpage = 1
repos_cnt = pagure.lib.query.search_projects(
flask.g.session, username=username, fork=False, count=True
)
pagination_metadata_repo = pagure.lib.query.get_pagination_metadata(
flask.request, repopage, per_page, repos_cnt, key_page="repopage"
)
repopage_start = (repopage - 1) * per_page
repopage_limit = per_page
repos = pagure.lib.query.search_projects(
flask.g.session,
username=username,
fork=False,
start=repopage_start,
limit=repopage_limit,
)
forks_cnt = pagure.lib.query.search_projects(
flask.g.session, username=username, fork=True, count=True
)
pagination_metadata_fork = pagure.lib.query.get_pagination_metadata(
flask.request, forkpage, per_page, forks_cnt, key_page="forkpage"
)
forkpage_start = (forkpage - 1) * per_page
forkpage_limit = per_page
forks = pagure.lib.query.search_projects(
flask.g.session,
username=username,
fork=True,
start=forkpage_start,
limit=forkpage_limit,
)
output["user"] = user.to_json(public=True)
output["user"]["avatar_url"] = pagure.lib.query.avatar_url_from_email(
user.default_email, size=16
)
output["repos"] = [repo.to_json(public=True) for repo in repos]
output["forks"] = [repo.to_json(public=True) for repo in forks]
output["repos_pagination"] = pagination_metadata_repo
output["forks_pagination"] = pagination_metadata_fork
jsonout = flask.jsonify(output)
jsonout.status_code = httpcode
return jsonout
@API.route("/user/<username>/issues")
@api_method
def api_view_user_issues(username):
"""
List user's issues
---------------------
List issues opened by or assigned to a specific user across all projects.
::
GET /api/0/user/<username>/issues
Parameters
^^^^^^^^^^
+---------------+---------+--------------+-----------------------------+
| Key | Type | Optionality | Description |
+===============+=========+==============+=============================+
| ``page`` | integer | Mandatory | | The page requested. |
| | | | Defaults to 1. |
+---------------+---------+--------------+-----------------------------+
| ``per_page`` | int | Optional | | The number of items |
| | | | to return per page. |
| | | | The maximum is 100. |
+---------------+---------+--------------+-----------------------------+
| ``status`` | string | Optional | | Filters the status of |
| | | | issues. Fetches all the |
| | | | issues if status is |
| | | | ``all``. Default: |
| | | | ``Open`` |
+---------------+---------+--------------+-----------------------------+
| ``tags`` | string | Optional | | A list of tags you wish to|
| | | | filter. If you want to |
| | | | filter for issues not |
| | | | having a tag, add an |
| | | | exclamation mark in front |
| | | | of it |
+---------------+---------+--------------+-----------------------------+
| ``milestones``| list of | Optional | | Filter the issues by |
| | strings | | milestone |
+---------------+---------+--------------+-----------------------------+
| ``no_stones`` | boolean | Optional | | If true returns only the |
| | | | issues having no |
| | | | milestone, if false |
| | | | returns only the issues |
| | | | having a milestone |
+---------------+---------+--------------+-----------------------------+
| ``since`` | string | Optional | | Filter the issues |
| | | | updated after this date. |
| | | | The date can either be |
| | | | provided as an unix date |
| | | | or in the format Y-M-D |
+---------------+---------+--------------+-----------------------------+
| ``order`` | string | Optional | | Set the ordering of the |
| | | | issues. This can be |
| | | | ``asc`` or ``desc``. |
| | | | Default: ``desc`` |
+---------------+---------+--------------+-----------------------------+
| ``order_key`` | string | Optional | | Set the ordering key. |
| | | | This can be ``assignee`` |
| | | | , ``last_updated`` or |
| | | | name of other column. |
| | | | Default: ``date_created`` |
+---------------+---------+--------------+-----------------------------+
| ``assignee`` | boolean | Optional | | A boolean of whether to |
| | | | return the issues |
| | | | assigned to this user |
| | | | or not. Defaults to True |
+---------------+---------+--------------+-----------------------------+
| ``author`` | boolean | Optional | | A boolean of whether to |
| | | | return the issues |
| | | | created by this user or |
| | | | not. Defaults to True |
+---------------+---------+--------------+-----------------------------+
| ``created`` | string | Optional | | Filter the issues returned|
| | | | by their creation date |
| | | | The date can be of |
| | | | specified either using |
| | | | a timestamp format or |
| | | | using the iso format for |
| | | | dates: yyyy-mm-dd. |
| | | | You can specify a start |
| | | | and a end date to this |
| | | | filter using start..end. |
+---------------+---------+--------------+-----------------------------+
| ``updated`` | string | Optional | | Filter the pull-requests |
| | | | returned by their update |
| | | | date. The date can be of |
| | | | specified either using |
| | | | a timestamp format or |
| | | | using the iso format for |
| | | | dates: yyyy-mm-dd. |
| | | | You can specify a start |
| | | | and a end date to this |
| | | | filter using start..end. |
+---------------+---------+--------------+-----------------------------+
| ``closed`` | string | Optional | | Filter the pull-requests |
| | | | returned by their closing |
| | | | date. The date can be of |
| | | | specified either using |
| | | | a timestamp format or |
| | | | using the iso format for |
| | | | dates: yyyy-mm-dd. |
| | | | You can specify a start |
| | | | and a end date to this |
| | | | filter using start..end. |
+---------------+---------+--------------+-----------------------------+
Sample response
^^^^^^^^^^^^^^^
::
{
"args": {
"assignee": true,
"author": true,
"milestones": [],
"no_stones": null,
"order": null,
"order_key": null,
"page": 1,
"since": null,
"status": null,
"tags": [],
"created": null,
"updated": null,
"closed": null,
},
"issues_assigned": [
{
"assignee": {
"fullname": "Anar Adilova",
"name": "anar"
},
"blocks": [],
"close_status": null,
"closed_at": null,
"closed_by": null,
"comments": [],
"content": "Test Issue",
"custom_fields": [],
"date_created": "1510124763",
"depends": [],
"id": 2,
"last_updated": "1510124763",
"milestone": null,
"priority": null,
"private": false,
"status": "Open",
"tags": [],
"title": "issue4",
"user": {
"fullname": "Anar Adilova",
"name": "anar"
}
}
],
"issues_created": [
{
"assignee": {
"fullname": "Anar Adilova",
"name": "anar"
},
"blocks": [],
"close_status": null,
"closed_at": null,
"closed_by": null,
"comments": [],
"content": "Test Issue",
"custom_fields": [],
"date_created": "1510124763",
"depends": [],
"id": 2,
"last_updated": "1510124763",
"milestone": null,
"priority": null,
"private": false,
"status": "Open",
"tags": [],
"title": "issue4",
"user": {
"fullname": "Anar Adilova",
"name": "anar"
}
}
],
"pagination_issues_assigned": {
"first": "http://localhost:5000/api/0/user/anar/issues?per_page=1&page=1",
"last": "http://localhost:5000/api/0/user/anar/issues?per_page=1&page=0",
"next": null,
"page": 1,
"pages": 0,
"per_page": 1,
"prev": null
},
"pagination_issues_created": {
"first": "http://localhost:5000/api/0/user/anar/issues?per_page=1&page=1",
"last": "http://localhost:5000/api/0/user/anar/issues?per_page=1&page=200",
"next": "http://localhost:5000/api/0/user/anar/issues?per_page=1&page=2",
"page": 1,
"pages": 200,
"per_page": 1,
"prev": null
},
"total_issues_assigned": 1,
"total_issues_created": 1
}
""" # noqa
milestone = flask.request.args.getlist("milestones", None)
no_stones = flask.request.args.get("no_stones", None)
if no_stones is not None:
no_stones = is_true(no_stones)
since = flask.request.args.get("since", None)
order = flask.request.args.get("order", None)
order_key = flask.request.args.get("order_key", None)
status = flask.request.args.get("status", None)
tags = flask.request.args.getlist("tags")
tags = [tag.strip() for tag in tags if tag.strip()]
created = flask.request.args.get("created")
updated = flask.request.args.get("updated")
closed = flask.request.args.get("closed")
try:
created_since, created_until = validate_date_range(created)
updated_since, updated_until = validate_date_range(updated)
closed_since, closed_until = validate_date_range(closed)
except pagure.exceptions.InvalidTimestampException:
raise pagure.exceptions.APIError(400, error_code=APIERROR.ETIMESTAMP)
except pagure.exceptions.InvalidDateformatException:
raise pagure.exceptions.APIError(400, error_code=APIERROR.EDATETIME)
page = get_page()
per_page = get_per_page()
assignee = flask.request.args.get("assignee", "").lower() not in [
"false",
"0",
"f",
]
author = flask.request.args.get("author", "").lower() not in [
"false",
"0",
"f",
]
offset = (page - 1) * per_page
limit = per_page
params = {
"session": flask.g.session,
"tags": tags,
"milestones": milestone,
"order": order,
"order_key": order_key,
"no_milestones": no_stones,
"offset": offset,
"limit": limit,
"created_since": created_since,
"created_until": created_until,
"updated_since": updated_since,
"updated_until": updated_until,
"closed_since": closed_since,
"closed_until": closed_until,
}
if status is not None:
if status.lower() == "all":
params.update({"status": None})
elif status.lower() == "closed":
params.update({"closed": True})
else:
params.update({"status": status})
else:
params.update({"status": "Open"})
updated_after = None
if since:
updated_after = validate_date(since)
params.update({"updated_after": updated_after})
issues_created = []
issues_created_pages = 1
issues_created_cnt = 0
pagination_issues_created = None
if author:
# Issues authored by this user
params_created = params.copy()
params_created.update({"author": username})
issues_created = pagure.lib.query.search_issues(**params_created)
params_created.update({"offset": None, "limit": None, "count": True})
issues_created_cnt = pagure.lib.query.search_issues(**params_created)
pagination_issues_created = pagure.lib.query.get_pagination_metadata(
flask.request, page, per_page, issues_created_cnt
)
issues_assigned = []
issues_assigned_pages = 1
issues_assigned_cnt = 0
pagination_issues_assigned = None
if assignee:
# Issues assigned to this user
params_assigned = params.copy()
params_assigned.update({"assignee": username})
issues_assigned = pagure.lib.query.search_issues(**params_assigned)
params_assigned.update({"offset": None, "limit": None, "count": True})
issues_assigned_cnt = pagure.lib.query.search_issues(**params_assigned)
pagination_issues_assigned = pagure.lib.query.get_pagination_metadata(
flask.request, page, per_page, issues_assigned_cnt
)
jsonout = flask.jsonify(
{
"pagination_issues_created": pagination_issues_created,
"pagination_issues_assigned": pagination_issues_assigned,
"total_issues_created_pages": issues_created_pages,
"total_issues_assigned_pages": issues_assigned_pages,
"total_issues_created": issues_created_cnt,
"total_issues_assigned": issues_assigned_cnt,
"issues_created": [
issue.to_json(public=True, with_project=True)
for issue in issues_created
],
"issues_assigned": [
issue.to_json(public=True, with_project=True)
for issue in issues_assigned
],
"args": {
"milestones": milestone,
"no_stones": no_stones,
"order": order,
"order_key": order_key,
"since": since,
"status": status,
"tags": tags,
"page": page,
"assignee": assignee,
"author": author,
"created": created,
"updated": updated,
"closed": closed,
},
}
)
return jsonout
@API.route("/user/<username>/activity/stats")
@api_method
def api_view_user_activity_stats(username):
"""
User activity stats
-------------------
Use this endpoint to retrieve activity stats about a specific user over
the last year.
::
GET /api/0/user/<username>/activity/stats
::
GET /api/0/user/ralph/activity/stats
GET /api/0/user/ralph/activity/stats?format=timestamp
Parameters
^^^^^^^^^^
+---------------+----------+--------------+----------------------------+
| Key | Type | Optionality | Description |
+===============+==========+==============+============================+
| ``username`` | string | Mandatory | | The username of the user |
| | | | whose activity you are |
| | | | interested in. |
+---------------+----------+--------------+----------------------------+
| ``format`` | string | Optional | | Allows changing the |
| | | | of the date/time returned|
| | | | from iso format to unix |
| | | | timestamp |
| | | | Can be: `timestamp` |
| | | | or `isoformat` |
+---------------+----------+--------------+----------------------------+
Sample response
^^^^^^^^^^^^^^^
::
{
"2015-11-04": 9,
"2015-11-06": 3,
"2015-11-09": 6,
"2015-11-13": 4,
"2015-11-15": 3,
"2015-11-18": 15,
"2015-11-19": 3,
"2015-11-20": 15,
"2015-11-26": 18,
"2015-11-30": 116,
"2015-12-02": 12,
"2015-12-03": 2
}
or::
{
"1446591600": 9,
"1446764400": 3,
"1447023600": 6,
"1447369200": 4,
"1447542000": 3,
"1447801200": 15,
"1447887600": 3,
"1447974000": 15,
"1448492400": 18,
"1448838000": 116,
"1449010800": 12,
"1449097200": 2
}
"""
date_format = flask.request.args.get("format", "isoformat")
tz = flask.request.args.get("tz", "UTC")
user = _get_user(username=username)
stats = pagure.lib.query.get_yearly_stats_user(
flask.g.session,
user,
datetime.datetime.utcnow().date() + datetime.timedelta(days=1),
tz=tz,
)
def format_date(d, tz):
if date_format == "timestamp":
# the reason we have this at all is the cal-heatmap js lib
# wants times as timestamps. We're trying to feed it a
# timestamp it will count as having happened on date 'd'.
# However, cal-heatmap always uses the browser timezone,
# so we have to be careful to produce a timestamp which
# falls on the correct date *in the browser timezone*. We
# aim for noon on the desired date.
try:
return arrow.get(d, tz).replace(hour=12).timestamp
except arrow.parser.ParserError:
# if tz is invalid for some reason, just go with UTC
return arrow.get(d).replace(hour=12).timestamp
else:
d = d.isoformat()
return d
stats = {format_date(d[0], tz): d[1] for d in stats}
jsonout = flask.jsonify(stats)
return jsonout
@API.route("/user/<username>/activity/<date>")
@api_method
def api_view_user_activity_date(username, date):
"""
User activity on a specific date
--------------------------------
Use this endpoint to retrieve activity information about a specific user
on the specified date.
::
GET /api/0/user/<username>/activity/<date>
::
GET /api/0/user/ralph/activity/2016-01-02
GET /api/0/user/ralph/activity/2016-01-02?grouped=true
Parameters
^^^^^^^^^^
+---------------+----------+--------------+----------------------------+
| Key | Type | Optionality | Description |
+===============+==========+==============+============================+
| ``username`` | string | Mandatory | | The username of the user |
| | | | whose activity you are |
| | | | interested in. |
+---------------+----------+--------------+----------------------------+
| ``date`` | string | Mandatory | | The date of interest, |
| | | | best provided in ISO |
| | | | format: YYYY-MM-DD |
+---------------+----------+--------------+----------------------------+
| ``grouped`` | boolean | Optional | | Whether or not to group |
| | | | the commits |
+---------------+----------+--------------+----------------------------+
Sample response
^^^^^^^^^^^^^^^
::
{
"activities": [
{
"date": "2016-02-24",
"date_created": "1456305852",
"description": "pingou created PR test#44",
"description_mk": "<p>pingou created PR <a href=\"/test/pull-request/44\" title=\"Update test_foo\">test#44</a></p>",
"id": 4067,
"user": {
"fullname": "Pierre-YvesC",
"name": "pingou"
}
},
{
"date": "2016-02-24",
"date_created": "1456305887",
"description": "pingou commented on PR test#44",
"description_mk": "<p>pingou commented on PR <a href=\"/test/pull-request/44\" title=\"Update test_foo\">test#44</a></p>",
"id": 4112,
"user": {
"fullname": "Pierre-YvesC",
"name": "pingou"
}
}
]
}
""" # noqa
grouped = is_true(flask.request.args.get("grouped"))
tz = flask.request.args.get("tz", "UTC")
try:
date = arrow.get(date)
date = date.strftime("%Y-%m-%d")
except arrow.parser.ParserError as err:
raise pagure.exceptions.APIError(
400, error_code=APIERROR.ENOCODE, error=str(err)
)
user = _get_user(username=username)
activities = pagure.lib.query.get_user_activity_day(
flask.g.session, user, date, tz=tz
)
js_act = []
if grouped:
commits = collections.defaultdict(list)
acts = []
for activity in activities:
if activity.log_type == "committed":
commits[activity.project.fullname].append(activity)
else:
acts.append(activity)
for project in commits:
if len(commits[project]) == 1:
tmp = dict(
description_mk=pagure.lib.query.text2markdown(
six.text_type(commits[project][0])
)
)
else:
tmp = dict(
description_mk=pagure.lib.query.text2markdown(
"@%s pushed %s commits to %s"
% (username, len(commits[project]), project)
)
)
js_act.append(tmp)
activities = acts
for act in activities:
activity = act.to_json(public=True)
activity["description_mk"] = pagure.lib.query.text2markdown(
six.text_type(act)
)
js_act.append(activity)
jsonout = flask.jsonify(dict(activities=js_act, date=date))
return jsonout
@API.route("/user/<username>/requests/filed")
@api_method
def api_view_user_requests_filed(username):
"""
List pull-requests filled by user
---------------------------------
Use this endpoint to retrieve a list of open pull requests a user has
filed over the entire pagure instance.
::
GET /api/0/user/<username>/requests/filed
::
GET /api/0/user/dudemcpants/requests/filed
Parameters
^^^^^^^^^^
+---------------+----------+--------------+-----------------------------+
| Key | Type | Optionality | Description |
+===============+==========+==============+=============================+
| ``username`` | string | Mandatory | | The username of the user |
| | | | whose activity you are |
| | | | interested in. |
+---------------+----------+--------------+-----------------------------+
| ``status`` | string | Optional | | Filter the status of |
| | | | pull requests. Default: |
| | | | ``Open`` (open pull |
| | | | requests), can be |
| | | | ``Closed`` for closed |
| | | | requests, ``Merged`` |
| | | | for merged requests, or |
| | | | ``Open`` for open |
| | | | requests. |
| | | | ``All`` returns closed, |
| | | | merged and open requests. |
+---------------+----------+--------------+-----------------------------+
| ``created`` | string | Optional | | Filter the pull-requests |
| | | | returned by their creation|
| | | | date. The date can be of |
| | | | specified either using |
| | | | a timestamp format or |
| | | | using the iso format for |
| | | | dates: yyyy-mm-dd. |
| | | | You can specify a start |
| | | | and a end date to this |
| | | | filter using start..end. |
+---------------+----------+--------------+-----------------------------+
| ``updated`` | string | Optional | | Filter the pull-requests |
| | | | returned by their update |
| | | | date. The date can be of |
| | | | specified either using |
| | | | a timestamp format or |
| | | | using the iso format for |
| | | | dates: yyyy-mm-dd. |
| | | | You can specify a start |
| | | | and a end date to this |
| | | | filter using start..end. |
+---------------+----------+--------------+-----------------------------+
| ``closed`` | string | Optional | | Filter the pull-requests |
| | | | returned by their closing |
| | | | date. The date can be of |
| | | | specified either using |
| | | | a timestamp format or |
| | | | using the iso format for |
| | | | dates: yyyy-mm-dd. |
| | | | You can specify a start |
| | | | and a end date to this |
| | | | filter using start..end. |
+---------------+----------+--------------+-----------------------------+
| ``page`` | integer | Mandatory | | The page requested. |
| | | | Defaults to 1. |
+---------------+----------+--------------+-----------------------------+
| ``per_page`` | int | Optional | | The number of items to |
| | | | return per page. |
| | | | The maximum is 100. |
+---------------+----------+--------------+-----------------------------+
Sample response
^^^^^^^^^^^^^^^
::
{
"args": {
"status": "open",
"username": "dudemcpants",
"page": 1,
"created": null,
"updated": null,
"closed": null,
},
"pagination": {
"first": "http://localhost:5000/api/0/user/dudemcpants/requests/filed?per_page=1&page=1",
"last": "http://localhost:5000/api/0/user/dudemcpants/requests/filed?per_page=1&page=61",
"next": "http://localhost:5000/api/0/user/dudemcpants/requests/filed?per_page=1&page=2",
"page": 1,
"pages": 61,
"per_page": 1,
"prev": null
},
"requests": [
{
"assignee": null,
"branch": "master",
"branch_from": "master",
"closed_at": null,
"closed_by": null,
"comments": [],
"commit_start": "3973fae98fc485783ca14f5c3612d85832185065",
"commit_stop": "3973fae98fc485783ca14f5c3612d85832185065",
"date_created": "1510227832",
"id": 2,
"initial_comment": null,
"last_updated": "1510227833",
"project": {
"access_groups": {
"admin": [],
"commit": [],
"ticket": []
},
"access_users": {
"admin": [],
"commit": [],
"owner": [
"ryanlerch"
],
"ticket": []
},
"close_status": [],
"custom_keys": [],
"date_created": "1510227638",
"date_modified": "1510227638",
"description": "this is a quick project",
"fullname": "aquickproject",
"id": 1,
"milestones": {},
"name": "aquickproject",
"namespace": null,
"parent": null,
"priorities": {},
"tags": [],
"url_path": "aquickproject",
"user": {
"fullname": "ryanlerch",
"name": "ryanlerch"
}
},
"remote_git": null,
"repo_from": {
"access_groups": {
"admin": [],
"commit": [],
"ticket": []
},
"access_users": {
"admin": [],
"commit": [],
"owner": [
"dudemcpants"
],
"ticket": []
},
"close_status": [],
"custom_keys": [],
"date_created": "1510227729",
"date_modified": "1510227729",
"description": "this is a quick project",
"fullname": "forks/dudemcpants/aquickproject",
"id": 2,
"milestones": {},
"name": "aquickproject",
"namespace": null,
"parent": {
"access_groups": {
"admin": [],
"commit": [],
"ticket": []
},
"access_users": {
"admin": [],
"commit": [],
"owner": [
"ryanlerch"
],
"ticket": []
},
"close_status": [],
"custom_keys": [],
"date_created": "1510227638",
"date_modified": "1510227638",
"description": "this is a quick project",
"fullname": "aquickproject",
"id": 1,
"milestones": {},
"name": "aquickproject",
"namespace": null,
"parent": null,
"priorities": {},
"tags": [],
"url_path": "aquickproject",
"user": {
"fullname": "ryanlerch",
"name": "ryanlerch"
}
},
"priorities": {},
"tags": [],
"url_path": "fork/dudemcpants/aquickproject",
"user": {
"fullname": "Dude McPants",
"name": "dudemcpants"
}
},
"status": "Open",
"title": "Update README.md",
"uid": "819e0b1c449e414fa291c914f28d73ec",
"updated_on": "1510227832",
"user": {
"fullname": "Dude McPants",
"name": "dudemcpants"
}
}
],
"total_requests": 1
}
""" # noqa
status = flask.request.args.get("status", "open")
created = flask.request.args.get("created")
updated = flask.request.args.get("updated")
closed = flask.request.args.get("closed")
try:
created_since, created_until = validate_date_range(created)
updated_since, updated_until = validate_date_range(updated)
closed_since, closed_until = validate_date_range(closed)
except pagure.exceptions.InvalidTimestampException:
raise pagure.exceptions.APIError(400, error_code=APIERROR.ETIMESTAMP)
except pagure.exceptions.InvalidDateformatException:
raise pagure.exceptions.APIError(400, error_code=APIERROR.EDATETIME)
page = get_page()
per_page = get_per_page()
offset = (page - 1) * per_page
limit = per_page
orig_status = status
if status.lower() == "all":
status = None
else:
status = status.capitalize()
pullrequests_cnt = pagure.lib.query.get_pull_request_of_user(
flask.g.session,
username=username,
status=status,
filed=username,
created_since=created_since,
created_until=created_until,
updated_since=updated_since,
updated_until=updated_until,
closed_since=closed_since,
closed_until=closed_until,
count=True,
)
pagination = pagure.lib.query.get_pagination_metadata(
flask.request, page, per_page, pullrequests_cnt
)
pullrequests = pagure.lib.query.get_pull_request_of_user(
flask.g.session,
username=username,
status=status,
filed=username,
created_since=created_since,
created_until=created_until,
updated_since=updated_since,
updated_until=updated_until,
closed_since=closed_since,
closed_until=closed_until,
offset=offset,
limit=limit,
)
pullrequestslist = [
pr.to_json(public=True, api=True) for pr in pullrequests
]
return flask.jsonify(
{
"total_requests": len(pullrequestslist),
"requests": pullrequestslist,
"args": {
"username": username,
"status": orig_status,
"page": page,
"created": created,
"updated": updated,
"closed": closed,
},
"pagination": pagination,
}
)
@API.route("/user/<username>/requests/actionable")
@api_method
def api_view_user_requests_actionable(username):
"""
List PRs actionable by user
---------------------------
Use this endpoint to retrieve a list of open pull requests a user is
able to action (e.g. merge) over the entire pagure instance.
::
GET /api/0/user/<username>/requests/actionable
::
GET /api/0/user/dudemcpants/requests/actionable
Parameters
^^^^^^^^^^
+---------------+----------+--------------+-----------------------------+
| Key | Type | Optionality | Description |
+===============+==========+==============+=============================+
| ``username`` | string | Mandatory | | The username of the user |
| | | | whose activity you are |
| | | | interested in. |
+---------------+----------+--------------+-----------------------------+
| ``created`` | string | Optional | | Filter the pull-requests |
| | | | returned by their creation|
| | | | date. The date can be of |
| | | | specified either using |
| | | | a timestamp format or |
| | | | using the iso format for |
| | | | dates: yyyy-mm-dd. |
| | | | You can specify a start |
| | | | and a end date to this |
| | | | filter using start..end. |
+---------------+----------+--------------+-----------------------------+
| ``updated`` | string | Optional | | Filter the pull-requests |
| | | | returned by their update |
| | | | date. The date can be of |
| | | | specified either using |
| | | | a timestamp format or |
| | | | using the iso format for |
| | | | dates: yyyy-mm-dd. |
| | | | You can specify a start |
| | | | and a end date to this |
| | | | filter using start..end. |
+---------------+----------+--------------+-----------------------------+
| ``closed`` | string | Optional | | Filter the pull-requests |
| | | | returned by their closing |
| | | | date. The date can be of |
| | | | specified either using |
| | | | a timestamp format or |
| | | | using the iso format for |
| | | | dates: yyyy-mm-dd. |
| | | | You can specify a start |
| | | | and a end date to this |
| | | | filter using start..end. |
+---------------+----------+--------------+-----------------------------+
| ``page`` | integer | Mandatory | | The page requested. |
| | | | Defaults to 1. |
+---------------+----------+--------------+-----------------------------+
| ``status`` | string | Optional | | Filter the status of |
| | | | pull requests. Default: |
| | | | ``Open`` (open pull |
| | | | requests), can be |
| | | | ``Closed`` for closed |
| | | | requests, ``Merged`` |
| | | | for merged requests, or |
| | | | ``Open`` for open |
| | | | requests. |
| | | | ``All`` returns closed, |
| | | | merged and open requests. |
+---------------+----------+--------------+-----------------------------+
Sample response
^^^^^^^^^^^^^^^
::
{
"args": {
"status": "open",
"username": "ryanlerch",
"page": 1,
"created": null,
"updated": null,
"closed": null,
},
"pagination": {
"first": "http://localhost:5000/api/0/user/ryanlerch/requests/actionable?per_page=1&page=1",
"last": "http://localhost:5000/api/0/user/ryanlerch/requests/actionable?per_page=1&page=61",
"next": "http://localhost:5000/api/0/user/ryanlerch/requests/actionable?per_page=1&page=2",
"page": 1,
"pages": 61,
"per_page": 1,
"prev": null
},
"requests": [
{
"assignee": null,
"branch": "master",
"branch_from": "master",
"closed_at": null,
"closed_by": null,
"comments": [],
"commit_start": "3973fae98fc485783ca14f5c3612d85832185065",
"commit_stop": "3973fae98fc485783ca14f5c3612d85832185065",
"date_created": "1510227832",
"id": 2,
"initial_comment": null,
"last_updated": "1510227833",
"project": {
"access_groups": {
"admin": [],
"commit": [],
"ticket": []
},
"access_users": {
"admin": [],
"commit": [],
"owner": [
"ryanlerch"
],
"ticket": []
},
"close_status": [],
"custom_keys": [],
"date_created": "1510227638",
"date_modified": "1510227638",
"description": "this is a quick project",
"fullname": "aquickproject",
"id": 1,
"milestones": {},
"name": "aquickproject",
"namespace": null,
"parent": null,
"priorities": {},
"tags": [],
"url_path": "aquickproject",
"user": {
"fullname": "ryanlerch",
"name": "ryanlerch"
}
},
"remote_git": null,
"repo_from": {
"access_groups": {
"admin": [],
"commit": [],
"ticket": []
},
"access_users": {
"admin": [],
"commit": [],
"owner": [
"dudemcpants"
],
"ticket": []
},
"close_status": [],
"custom_keys": [],
"date_created": "1510227729",
"date_modified": "1510227729",
"description": "this is a quick project",
"fullname": "forks/dudemcpants/aquickproject",
"id": 2,
"milestones": {},
"name": "aquickproject",
"namespace": null,
"parent": {
"access_groups": {
"admin": [],
"commit": [],
"ticket": []
},
"access_users": {
"admin": [],
"commit": [],
"owner": [
"ryanlerch"
],
"ticket": []
},
"close_status": [],
"custom_keys": [],
"date_created": "1510227638",
"date_modified": "1510227638",
"description": "this is a quick project",
"fullname": "aquickproject",
"id": 1,
"milestones": {},
"name": "aquickproject",
"namespace": null,
"parent": null,
"priorities": {},
"tags": [],
"url_path": "aquickproject",
"user": {
"fullname": "ryanlerch",
"name": "ryanlerch"
}
},
"priorities": {},
"tags": [],
"url_path": "fork/dudemcpants/aquickproject",
"user": {
"fullname": "Dude McPants",
"name": "dudemcpants"
}
},
"status": "Open",
"title": "Update README.md",
"uid": "819e0b1c449e414fa291c914f28d73ec",
"updated_on": "1510227832",
"user": {
"fullname": "Dude McPants",
"name": "dudemcpants"
}
}
],
"total_requests": 1
}
""" # noqa
status = flask.request.args.get("status", "open")
created = flask.request.args.get("created")
updated = flask.request.args.get("updated")
closed = flask.request.args.get("closed")
try:
created_since, created_until = validate_date_range(created)
updated_since, updated_until = validate_date_range(updated)
closed_since, closed_until = validate_date_range(closed)
except pagure.exceptions.InvalidTimestampException:
raise pagure.exceptions.APIError(400, error_code=APIERROR.ETIMESTAMP)
except pagure.exceptions.InvalidDateformatException:
raise pagure.exceptions.APIError(400, error_code=APIERROR.EDATETIME)
page = get_page()
per_page = get_per_page()
offset = (page - 1) * per_page
limit = per_page
orig_status = status
if status.lower() == "all":
status = None
else:
status = status.capitalize()
pullrequests_cnt = pagure.lib.query.get_pull_request_of_user(
flask.g.session,
username=username,
status=status,
actionable=username,
created_since=created_since,
created_until=created_until,
updated_since=updated_since,
updated_until=updated_until,
closed_since=closed_since,
closed_until=closed_until,
count=True,
)
pagination = pagure.lib.query.get_pagination_metadata(
flask.request, page, per_page, pullrequests_cnt
)
pullrequests = pagure.lib.query.get_pull_request_of_user(
flask.g.session,
username=username,
status=status,
actionable=username,
created_since=created_since,
created_until=created_until,
updated_since=updated_since,
updated_until=updated_until,
closed_since=closed_since,
closed_until=closed_until,
offset=offset,
limit=limit,
)
pullrequestslist = [
pr.to_json(public=True, api=True) for pr in pullrequests
]
return flask.jsonify(
{
"total_requests": len(pullrequestslist),
"requests": pullrequestslist,
"args": {
"username": username,
"status": orig_status,
"page": page,
"created": created,
"updated": updated,
"closed": closed,
},
"pagination": pagination,
}
)