Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add issues to homepage (static version) #129

Merged
merged 32 commits into from
May 28, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
cb6b69b
Add Ember, Ember Model, Handlebars deps.
May 20, 2014
75eaccb
Use Ember.prod builds from Grunt, serve debug in dev.
May 20, 2014
44277a2
Rename new_index method to index.
May 20, 2014
8709fe2
Add login/logout from header + avatar.
May 21, 2014
5026754
Pull out feedback/login bar into a topbar.html partial.
May 21, 2014
3bb2f90
Pull out the Browse Issues section into its own partial.
May 21, 2014
f9a0798
Initial styles for Browse Issues container + key.
May 21, 2014
0ffb393
Padding tweak for small viewports.
May 21, 2014
bb21aff
Final tweak to issues key at smaller viewports.
May 21, 2014
d19c765
Style tweaks for issues.
May 21, 2014
23a641d
Style flash messages; fadeOut after 1500ms.
May 22, 2014
aeb8de7
Add bottom margin to issue containers.
May 22, 2014
947fb6b
Bugsahoy is the wrong there here, IMO.
May 23, 2014
4e63af9
Use a better name for the users.db.
May 23, 2014
32582e1
Store github username and avatar uri in our user.db.
May 23, 2014
2c33d44
Add our app.js to the Grunt file.
May 23, 2014
e4fe0b7
Add all changes related to Ember on the homepage.
May 27, 2014
1d4eb97
Remove Ember deps from Gruntfile & main layout for now.
May 27, 2014
b419c9b
Add methods to fine "contactready", "needsdiagnosis" and "my" issues.
May 27, 2014
1049bb0
Add in templating hooks to render "browse issues" section.
May 27, 2014
bfb5bd3
Remove username hack from topbar.
May 27, 2014
42f90b2
Pass in data to Browse Issues section from view controller.
May 27, 2014
29a23be
Actually filter the not-contact-ready issues.
May 27, 2014
89d6d0a
Update comment.
May 28, 2014
35d18bc
Uh, I should return JSON not strings.
May 28, 2014
d43ecfb
We're returning JSON from the response now.
May 28, 2014
5114bf4
One liner now.
May 28, 2014
24c5e4a
Small bug in contact ready template.
May 28, 2014
f5bee97
Rewrite of issues.py:
May 28, 2014
22aa0e6
Remove leftover debug statements.
May 28, 2014
35936a5
Add some tests for issue filtering and adding status class.
May 28, 2014
367287a
Lint Gruntfile and app.js
May 28, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions Gruntfile.js
Original file line number Diff line number Diff line change
Expand Up @@ -64,13 +64,16 @@ module.exports = function(grunt) {
browser: true,
globals: {
jQuery: true,
$: true
$: true,
Ember: true,
App: true
}
},
beforeconcat: [
'Gruntfile.js',
'<%= jsPath %>/lib/homepage.js',
'<%= jsPath %>/lib/bugform.js'
'<%= jsPath %>/lib/bugform.js',
'<%= jsPath %>/lib/app.js'
]
},
watch: {
Expand Down
54 changes: 54 additions & 0 deletions tests/test_issue_filtering.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# This Source Code Form is subject to the terms of the Mozilla Public
# License, v. 2.0. If a copy of the MPL was not distributed with this
# file, You can obtain one at http://mozilla.org/MPL/2.0/.

'''Tests for filtering results from API calls.'''

import unittest
import os.path
import sys

# Add webcompat module to import path
sys.path.append(os.path.realpath(os.pardir))
import webcompat
from webcompat.issues import filter_needs_diagnosis, add_status_class

ISSUES = [{u'body': u'<!-- @browser: Firefox -->\n\n**URL**: www.internet.biz\n**Browser**: Firefox\n**Version**: 32.0\n**Problem type**: Unknown\n**Site owner**: Unknown\n\n**Steps to Reproduce**\n1) Navigate to: www.internet.biz\r\n2) \u2026\r\n\r\nExpected Behavior:\r\nActual Behavior:\r\n', u'labels': [{u'url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/labels/contactready', u'color': u'eb6420', u'name': u'contactready'}], u'title': u'Site is confusing', u'url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50', u'labels_url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50/labels{/name}', u'created_at': u'2014-05-27T20:24:51Z', u'events_url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50/events', u'comments_url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50/comments', u'html_url': u'https://github.com/miketaylr/nobody-look-at-this/issues/50', u'comments': 0, u'number': 50, u'updated_at': u'2014-05-27T21:54:00Z', u'assignee': None, u'state': u'open', u'user': {u'following_url': u'https://api.github.com/users/miketaylr/following{/other_user}', u'events_url': u'https://api.github.com/users/miketaylr/events{/privacy}', u'organizations_url': u'https://api.github.com/users/miketaylr/orgs', u'url': u'https://api.github.com/users/miketaylr', u'gists_url': u'https://api.github.com/users/miketaylr/gists{/gist_id}', u'html_url': u'https://github.com/miketaylr', u'subscriptions_url': u'https://api.github.com/users/miketaylr/subscriptions', u'avatar_url': u'https://avatars.githubusercontent.com/u/67283?', u'repos_url': u'https://api.github.com/users/miketaylr/repos', u'received_events_url': u'https://api.github.com/users/miketaylr/received_events', u'gravatar_id': u'929d3002e426ec2e88d89637d3f5f8ba', u'starred_url': u'https://api.github.com/users/miketaylr/starred{/owner}{/repo}', u'site_admin': False, u'login': u'miketaylr', u'type': u'User', u'id': 67283, u'followers_url': u'https://api.github.com/users/miketaylr/followers'}, u'milestone': None, u'closed_at': None, u'id': 34409616}, {u'body': u'<!-- @browser: Firefox -->\n\n**URL**: www.internet.biz\n**Browser**: Firefox\n**Version**: 32.0\n**Problem type**: Unknown\n**Site owner**: Unknown\n\n**Steps to Reproduce**\n1) Navigate to: www.internet.biz\r\n2) \u2026\r\n\r\nExpected Behavior:\r\nActual Behavior:\r\n', u'labels': [], u'title': u'Site is confusing', u'url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50', u'labels_url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50/labels{/name}', u'created_at': u'2014-05-27T20:24:51Z', u'events_url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50/events', u'comments_url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50/comments', u'html_url': u'https://github.com/miketaylr/nobody-look-at-this/issues/50', u'comments': 0, u'number': 50, u'updated_at': u'2014-05-27T21:54:00Z', u'assignee': None, u'state': u'open', u'user': {u'following_url': u'https://api.github.com/users/miketaylr/following{/other_user}', u'events_url': u'https://api.github.com/users/miketaylr/events{/privacy}', u'organizations_url': u'https://api.github.com/users/miketaylr/orgs', u'url': u'https://api.github.com/users/miketaylr', u'gists_url': u'https://api.github.com/users/miketaylr/gists{/gist_id}', u'html_url': u'https://github.com/miketaylr', u'subscriptions_url': u'https://api.github.com/users/miketaylr/subscriptions', u'avatar_url': u'https://avatars.githubusercontent.com/u/67283?', u'repos_url': u'https://api.github.com/users/miketaylr/repos', u'received_events_url': u'https://api.github.com/users/miketaylr/received_events', u'gravatar_id': u'929d3002e426ec2e88d89637d3f5f8ba', u'starred_url': u'https://api.github.com/users/miketaylr/starred{/owner}{/repo}', u'site_admin': False, u'login': u'miketaylr', u'type': u'User', u'id': 67283, u'followers_url': u'https://api.github.com/users/miketaylr/followers'}, u'milestone': None, u'closed_at': None, u'id': 34409616}, {u'body': u'<!-- @browser: Firefox -->\n\n**URL**: www.internet.biz\n**Browser**: Firefox\n**Version**: 32.0\n**Problem type**: Unknown\n**Site owner**: Unknown\n\n**Steps to Reproduce**\n1) Navigate to: www.internet.biz\r\n2) \u2026\r\n\r\nExpected Behavior:\r\nActual Behavior:\r\n', u'labels': [], u'title': u'Site is confusing', u'url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50', u'labels_url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50/labels{/name}', u'created_at': u'2014-05-27T20:24:51Z', u'events_url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50/events', u'comments_url': u'https://api.github.com/repos/miketaylr/nobody-look-at-this/issues/50/comments', u'html_url': u'https://github.com/miketaylr/nobody-look-at-this/issues/50', u'comments': 0, u'number': 50, u'updated_at': u'2014-05-27T21:54:00Z', u'assignee': None, u'state': u'open', u'user': {u'following_url': u'https://api.github.com/users/miketaylr/following{/other_user}', u'events_url': u'https://api.github.com/users/miketaylr/events{/privacy}', u'organizations_url': u'https://api.github.com/users/miketaylr/orgs', u'url': u'https://api.github.com/users/miketaylr', u'gists_url': u'https://api.github.com/users/miketaylr/gists{/gist_id}', u'html_url': u'https://github.com/miketaylr', u'subscriptions_url': u'https://api.github.com/users/miketaylr/subscriptions', u'avatar_url': u'https://avatars.githubusercontent.com/u/67283?', u'repos_url': u'https://api.github.com/users/miketaylr/repos', u'received_events_url': u'https://api.github.com/users/miketaylr/received_events', u'gravatar_id': u'929d3002e426ec2e88d89637d3f5f8ba', u'starred_url': u'https://api.github.com/users/miketaylr/starred{/owner}{/repo}', u'site_admin': False, u'login': u'miketaylr', u'type': u'User', u'id': 67283, u'followers_url': u'https://api.github.com/users/miketaylr/followers'}, u'milestone': None, u'closed_at': '2014-05-27T22:54:00Z', u'id': 34409616}]


class TestStatusClass(unittest.TestCase):
def setUp(self):
webcompat.app.config['TESTING'] = True
self.app = webcompat.app.test_client()
self.issues = ISSUES

def tearDown(self):
pass

def test_add_status_class(self):
add_status_class(self.issues)
self.assertEqual(self.issues[0]["status_class"], 'issue-contactready')
self.assertEqual(self.issues[1]["status_class"],
'issue-needs-diagnosis')
self.assertEqual(self.issues[2]["status_class"], 'issue-closed')


class TestFilterNeedsDiagnosis(unittest.TestCase):
def setUp(self):
webcompat.app.config['TESTING'] = True
self.app = webcompat.app.test_client()
self.issues = ISSUES

def tearDown(self):
pass

def test_filter(self):
filtered = filter_needs_diagnosis(self.issues)
self.assertEqual(len(filtered), 2)
self.assertIsInstance(filtered[0], dict)


if __name__ == '__main__':
unittest.main()
2 changes: 1 addition & 1 deletion webcompat/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
app = Flask(__name__, static_url_path='')
app.config.from_object('config')
engine = create_engine('sqlite:///' + os.path.join(app.config['BASE_DIR'],
'github-session.db'))
'users.db'))

github = GitHub(app)

Expand Down
87 changes: 80 additions & 7 deletions webcompat/issues.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,17 +13,90 @@
from form import build_formdata
from webcompat import github, app

URI = app.config['ISSUES_REPO_URI']
REPO_URI = app.config['ISSUES_REPO_URI']
TOKEN = app.config['BOT_OAUTH_TOKEN']


def proxy_request(method, path_mod='', data=None):
'''Make a GitHub API request with a bot's OAuth token, for non-logged in
users. `path`, if included, will be appended to the end of the URI.
Optionally pass in POST data via the `data` arg.'''
headers = {'Authorization': 'token {0}'.format(TOKEN)}
req_uri = 'https://api.github.com/repos/{0}{1}'.format(REPO_URI, path_mod)
req = getattr(requests, method)
if data:
return req(req_uri, data=data, headers=headers).json()
else:
return req(req_uri, headers=headers).json()


def report_issue(form):
return github.post('repos/{0}'.format(URI), build_formdata(form))
'''Report an issue, as a logged in user.'''
return github.post('repos/{0}'.format(REPO_URI), build_formdata(form))


def proxy_report_issue(form):
'''Reports an issue using a bot's auth token, on behalf of a user.'''
headers = {'Authorization': 'token {0}'.format(TOKEN)}
uri = 'https://api.github.com/repos/{0}'.format(URI)
return requests.post(uri, data=json.dumps(build_formdata(form)),
headers=headers)
'''Report an issue, on behalf of a user.'''
return proxy_request('post', data=json.dumps(build_formdata(form)))


def add_status_class(issues):
'''Add a "status_class" property to each issue to be used by CSS.'''
for issue in issues:
# default is needs-diagnosis
issue['status_class'] = u'issue-needs-diagnosis'
for label in issue.get('labels'):
if 'contactready' in label.get('name'):
issue['status_class'] = u'issue-contactready'
break
if issue.get('closed_at'):
issue['status_class'] = u'issue-closed'
return issues


def get_user_issues(username):
'''Return 8 issues in the repo reported by `username` (the creator
in the JSON response.'''
user_issues_uri = 'repos/{0}?creator={1}&state=all'.format(REPO_URI,
username)
issues = github.get(user_issues_uri)
return add_status_class(issues)[0:8]


def get_contact_ready():
'''Return all issues with a "contactready" label.'''
uri = 'repos/{0}?labels=contactready'.format(REPO_URI)
issues = github.get(uri)
return issues[0:4]


def proxy_get_contact_ready():
'''Return a proxied request for all issues with a "contactready" label.'''
issues = proxy_request('get', '?labels=contactready')
return issues[0:4]


def filter_needs_diagnosis(issues):
'''For our purposes, "needs diagnosis" means anything that isn't an issue
with a "contactready" label.'''
def not_contactready(issue):
if issue.get('labels') == []:
return True
else:
for label in issue.get('labels'):
if not 'contactready' in label.get('name'):
return True

return [issue for issue in issues if not_contactready(issue)]


def get_needs_diagnosis():
'''Return the first 4 issues that need diagnosis.'''
issues = github.get('repos/{0}'.format(REPO_URI))
return filter_needs_diagnosis(issues)[0:4]


def proxy_get_needs_diagnosis():
'''Return the first 4 issues that need diagnosis.'''
issues = proxy_request('get')
return filter_needs_diagnosis(issues)[0:4]
4 changes: 3 additions & 1 deletion webcompat/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ class User(Base):
# Current Github username limit is 39 chars
# Let's assume it might change in the future?
username = Column(String(64), unique=True)
avatar_url = Column(String(128))
github_access_token = Column(Integer)
gravatar_id = Column(String(64))

def __init__(self, github_access_token):
self.github_access_token = github_access_token
self.username = ''
self.avatar_url = ''


Base.metadata.create_all(bind=engine)
137 changes: 120 additions & 17 deletions webcompat/static/css/main.css
Original file line number Diff line number Diff line change
Expand Up @@ -174,28 +174,50 @@ footer .follow {

/*--------Flash Messages---------*/
.flash {
border: 0.15em solid #D47039;
position: absolute;
left: 50%;
top: 2em;
top: 0;
left:0;
right: 0;
opacity: 0.8;
background-color: white;
padding: 1em;
border-radius: 5px;
color: #000;
margin-left: -25%;
width: 50%;
padding: 0.5em 1em;
text-align: center;
color: #000;
}

/*--------Homepage Styles--------*/
#feedback {
background: #fff;
color: #000;
text-align: center;
margin: 0 0.1em;
padding: 0.2em;
background: #FBC55F;
padding: 0.5em 0;
}
#feedback a {
color: #fff;
padding: 0 5em;
text-decoration: none !important;
}
#feedback a:hover {
text-decoration: underline !important;
}
#feedback .container {
background-image: none !important;
}
#feedback .pull-right {
display: inline-block;
position: relative;
top: 0.1em;
right: 5em;
}
#feedback .pull-right a {
padding-left: 0;
padding-right: 2em;
}
.avatar {
width: 20px;
height: 20px;
display: inline-block;
position: absolute;
top: 0;
right: 0;
border-radius: 3px;
}
#hero {
background-color: #f8c41c;
Expand Down Expand Up @@ -277,6 +299,56 @@ form .btn-link, form .btn-link:hover {
.form-closed {
display: none;
}
/*-------Browse Issues------*/
#browse-issues-wrapper {
background-color: #f0f0f0;
}
#browse-issues h2 {
display: inline;
margin-right: 0.5em;
}
.key {
position: relative;
bottom:0.1em;
}
.key span {
padding: 0.3em 1em;
}
.issue {
background-color: #fff;
padding: 0.5em 1em;
margin-bottom: 1em;
}
.issue-contactready {
border-left: 5px solid #a1ebbf;
}
.issue-needs-diagnosis {
border-left: 5px solid #ff8364;
}
.issue-closed {
border-left: 5px solid #6fc7fa;
}
.issue-number {
font-weight: 400;
}
.issue-title {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-bottom:0.5em;
}
.issue-metadata {
font-size: 12px;
}
.more-issues {
margin-bottom: -23em;
text-align: center;
clear: left;
}
#my-issues h3 {
padding-left: 15px;
padding-right: 15px;
}
/*---Search on Github---*/
input[type=search] {
float: left;
Expand All @@ -286,7 +358,7 @@ input[type=search] {
cursor: pointer;
color: #fdf3dd;
}
#howitworks, #join {
#howitworks, #join, #browse-issues {
padding: 3em 5em;
}
#threesteps {
Expand Down Expand Up @@ -402,7 +474,7 @@ form .button {
.row {
padding: .5em;
}
.row:last-child p {
.row:last-child p:not(.issue-metadata) {
margin-bottom: 0.5em;
color: #000;
}
Expand Down Expand Up @@ -485,6 +557,11 @@ form .button {
padding: 0;
}
/*------Media Queries------*/
@media all and (max-width: 990px) {
.key span {
padding: 0 0 0 0.5em;
}
}
@media all and (max-width: 950px) {
.bignumber {
padding-right: 0.5em;
Expand All @@ -494,6 +571,10 @@ form .button {
#maintitle h1 {
font-size: 3em;
}
.key {
display: block;
margin-top: 1em;
}
.bignumber {
padding-bottom: 0;
}
Expand Down Expand Up @@ -539,10 +620,17 @@ form .button {
#maintitle {
padding: 3em 1em 3em;
}
#feedback .pull-right {
right: 1em;
}
#feedback a {
padding-right: 1em;
padding-left: 1em;
}
#report-bug.opened {
top: 1.25em;
}
#howitworks, #join {
#howitworks, #join, #browse-issues {
padding: 2em 1em;
}
footer {
Expand All @@ -559,4 +647,19 @@ form .button {
float: left;
margin-left: -10px;
}
}

@media all and (max-width: 330px) {
#feedback a {
font-size: 13px;
}
#feedback .pull-right {
right: 0;
}
#feedback .avatar {
display: none;
}
#feedback .pull-right a {
padding-right: 0 !important;
}
}
Loading