Skip to content

Commit

Permalink
Enhance the community website homepage
Browse files Browse the repository at this point in the history
The enhancement includes addition of materialize
css, JQuery, responsiveness, and easy-navigation
of website features. The easy-navigatibility is
achieved by adding a navbar with display of meta
-review and gamification leaderboard on homepage.
Apart from this, the activity graph url is omitted
from website by displaying the graph itslef on the
homepage on large devices.

Closes coala#255
  • Loading branch information
KVGarg committed Aug 4, 2019
1 parent c81d98c commit ae718af
Show file tree
Hide file tree
Showing 28 changed files with 983 additions and 234 deletions.
1 change: 1 addition & 0 deletions .ci/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ python manage.py fetch_deployed_data _site $ISSUES_JSON \

python manage.py migrate
python manage.py import_contributors_data
python manage.py create_org_cluster_map_and_activity_graph org_map
python manage.py import_issues_data
python manage.py import_merge_requests_data
python manage.py create_config_data
Expand Down
3 changes: 2 additions & 1 deletion .coafile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[all]
files = **.py, **.js, **.sh
ignore = .git/**, **/__pycache__/**, gci/client.py, */migrations/**, private/*, openhub/**
ignore = .git/**, **/__pycache__/**, gci/client.py, */migrations/**, private/*, openhub/**, **/leaflet_dist/**
max_line_length = 80
use_spaces = True
preferred_quotation = '
Expand Down Expand Up @@ -42,6 +42,7 @@ files = static/**/*.js
bears = JSHintBear
allow_unused_variables = True
javascript_strictness = False
environment_jquery = True

[all.yml]
bears = YAMLLintBear
Expand Down
2 changes: 1 addition & 1 deletion .moban.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ packages:
- log
- meta_review
- model
- twitter
- unassigned_issues

dependencies:
- getorg~=0.3.1
- git+https://gitlab.com/coala/coala-utils.git
- git-url-parse
- django>2.1,<2.2
Expand Down
1 change: 0 additions & 1 deletion .nocover.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ nocover_file_globs:
- meta_review/handler.py
- model/*.py
- openhub/*.py
- twitter/*.py
# Optional coverage. Once off scripts.
- inactive_issues/inactive_issues_scraper.py
- unassigned_issues/unassigned_issues_scraper.py
Expand Down
5 changes: 3 additions & 2 deletions activity/scraper.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def get_data(self):
return self.data


def activity_json(request):
def activity_json(filename):

org_name = get_org_name()

Expand All @@ -152,4 +152,5 @@ def activity_json(request):
real_data = Scraper(parsed_json['issues'], datetime.datetime.today())
real_data = real_data.get_data()

return HttpResponse(json.dumps(real_data))
with open(filename, 'w+') as f:
json.dump(real_data, f, indent=4)
21 changes: 0 additions & 21 deletions community/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,10 @@
from django_distill import distill_url
from django.conf.urls.static import static
from django.conf import settings
from django.views.generic import TemplateView

from community.views import HomePageView, info
from gci.views import index as gci_index
from gci.feeds import LatestTasksFeed as gci_tasks_rss
from activity.scraper import activity_json
from twitter.view_twitter import index as twitter_index
from log.view_log import index as log_index
from data.views import index as contributors_index
from gamification.views import index as gamification_index
Expand Down Expand Up @@ -87,18 +84,6 @@ def get_organization():
distill_func=get_index,
distill_file='info.txt',
),
distill_url(
r'static/activity-data.json', activity_json,
name='activity_json',
distill_func=get_index,
distill_file='static/activity-data.json',
),
distill_url(
r'activity/', TemplateView.as_view(template_name='activity.html'),
name='activity',
distill_func=get_index,
distill_file='activity/index.html',
),
distill_url(
r'gci/tasks/rss.xml', gci_tasks_rss(),
name='gci-tasks-rss',
Expand All @@ -111,12 +96,6 @@ def get_organization():
distill_func=get_index,
distill_file='gci/index.html',
),
distill_url(
r'twitter/', twitter_index,
name='twitter',
distill_func=get_index,
distill_file='twitter/index.html',
),
distill_url(
r'log/', log_index,
name='log',
Expand Down
108 changes: 99 additions & 9 deletions community/views.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,118 @@
from django.http import HttpResponse
from django.views.generic.base import TemplateView
import logging

import requests

from trav import Travis

from django.http import HttpResponse
from django.views.generic.base import TemplateView

from .git import (
get_deploy_url,
get_org_name,
get_owner,
get_upstream_deploy_url,
get_remote_url
)
from data.models import Team
from gamification.models import Participant as GamificationParticipant
from meta_review.models import Participant as MetaReviewer


def initialize_org_context_details():
org_name = get_org_name()
org_details = {
'name': org_name,
'blog_url': f'https://blog.{org_name}.io/',
'twitter_url': f'https://twitter.com/{org_name}_io/',
'facebook_url': f'https://www.facebook.com/{org_name}Analyzer',
'repo_url': get_remote_url().href,
'docs': f'https://{org_name}.io/docs',
'newcomer_docs': f'https://{org_name}.io/newcomer',
'coc': f'https://{org_name}.io/coc',
'logo_url': (f'https://api.{org_name}.io/en/latest/_static/images/'
f'{org_name}_logo.svg'),
'gitter_chat': f'https://gitter.im/{org_name}/{org_name}/',
'github_core_repo': f'https://github.com/{org_name}/{org_name}/',
'licence_type': 'GNU AGPL v3.0'
}
return org_details


def get_header_and_footer(context):
context['isTravis'] = Travis.TRAVIS
context['travisLink'] = Travis.TRAVIS_BUILD_WEB_URL
context['org'] = initialize_org_context_details()
print('Running on Travis: {}, build link: {}'.format(context['isTravis'],
context['travisLink']
))
return context


class HomePageView(TemplateView):
template_name = 'index.html'

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['isTravis'] = Travis.TRAVIS
context['travisLink'] = Travis.TRAVIS_BUILD_WEB_URL
def get_team_details(self, org_name):
teams = [
f'{org_name} newcomers',
f'{org_name} developers',
f'{org_name} admins'
]
team_details = {}
for team_name in teams:
team = Team.objects.get(name=team_name)
contributors_count = team.contributors.count()
team_details[
team_name.replace(org_name, '').strip().capitalize()
] = contributors_count
return team_details

def get_quote_of_the_day(self):

try:
qod = requests.get('http://quotes.rest/qod?category=inspire')
qod.raise_for_status()
except requests.HTTPError as err:
error_info = f'HTTPError while fetching Quote of the day! {err}'
logging.error(error_info)
return

print('Running on Travis: {}, build link: {}'.format(
context['isTravis'],
context['travisLink']))
qod_data = qod.json()
return {
'quote': qod_data['contents']['quotes'][0]['quote'],
'author': qod_data['contents']['quotes'][0]['author'],
}

def get_top_meta_review_users(self, count):
participants = MetaReviewer.objects.all()[:count]
return participants

def get_top_gamification_users(self, count):
return enumerate(GamificationParticipant.objects.all()[:count])

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context = get_header_and_footer(context)
org_name = context['org']['name']
context['org']['team_details'] = dict(self.get_team_details(org_name))
about_org = (f'{org_name} (always spelled with a lowercase c!) is one'
' of the welcoming open-source organizations for'
f' newcomers. {org_name} stands for “COde AnaLysis'
' Application” as it works well with animals and thus is'
' well visualizable which makes it easy to memorize.'
f' {org_name} provides a unified interface for linting'
' and fixing the code with a single configuration file,'
' regardless of the programming languages used. You can'
f' use {org_name} from within your favorite editor,'
' integrate it with your CI and, get the results as JSON'
', or customize it to your needs with its flexible'
' configuration syntax.')
context['org']['about'] = about_org
context['quote_details'] = self.get_quote_of_the_day()
context['top_meta_review_users'] = self.get_top_meta_review_users(
count=5)
context['top_gamification_users'] = self.get_top_gamification_users(
count=5)
return context


Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
from django.core.management.base import BaseCommand

from data.org_cluster_map_handler import handle as org_cluster_map_handler
from activity.scraper import activity_json


class Command(BaseCommand):
help = 'Create a cluster map using contributors geolocation'

def add_arguments(self, parser):
parser.add_argument('output_dir', nargs='?', type=str)

def handle(self, *args, **options):
output_dir = options.get('output_dir')
if not output_dir:
org_cluster_map_handler()
else:
org_cluster_map_handler(output_dir)
# Fetch & Store data for activity graph to be displayed on home-page
activity_json('static/activity-data.js')
18 changes: 18 additions & 0 deletions data/migrations/0006_auto_20190801_1752.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-08-01 17:52

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('data', '0005_auto_20190801_1442'),
]

operations = [
migrations.AlterField(
model_name='contributor',
name='teams',
field=models.ManyToManyField(related_name='contributors', to='data.Team'),
),
]
2 changes: 1 addition & 1 deletion data/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ class Contributor(models.Model):
reviews = models.IntegerField(default=None, null=True)
issues_opened = models.IntegerField(default=None, null=True)
location = models.TextField(default=None, null=True)
teams = models.ManyToManyField(Team)
teams = models.ManyToManyField(Team, related_name='contributors')

def __str__(self):
return self.login
Expand Down
82 changes: 82 additions & 0 deletions data/org_cluster_map_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import os
import json

import logging

import getorg

from data.models import Contributor


def handle(output_dir='cluster_map'):
"""
Creates a organization cluster map using the contributors location
stored in the database
:param output_dir: Directory where all the required CSS and JS files
are copied by 'getorg' package
"""
logger = logging.getLogger(__name__)
logger.info("'cluster_map/' is the default directory for storing"
" organization map related files. If arg 'output_dir'"
' not provided it will be used as a default directory by'
" 'getorg' package.")

# For creating the organization map, the 'getorg' uses a 'Nominatim' named
# package which geocodes the contributor location and then uses that class
# to create the map. Since, we're not dealing with that function which use
# that 'Nominatim' package because we're fetching a JSON data and storing
# it in our db. Therefore, defining our own simple class that can aid us
# to create a cluster map.
class Location:

def __init__(self, longitude, latitude):
self.longitude = longitude
self.latitude = latitude

org_location_dict = {}

for contrib in Contributor.objects.filter(location__isnull=False):
user_location = json.loads(contrib.location)
location = Location(user_location['longitude'],
user_location['latitude'])
org_location_dict[contrib.login] = location
logger.debug(f'{contrib.login} location {user_location} added on map')
getorg.orgmap.output_html_cluster_map(org_location_dict,
folder_name=output_dir)

move_and_make_changes_in_files(output_dir)


def move_and_make_changes_in_files(output_dir):
"""
Move static files from 'output_dir' to django static folder which
is being required by the map.html which is being auto-generated
by getorg.
:param output_dir: Directory from where the files have to be moved
"""

move_leaflet_dist_folder(output_dir)

os.rename(
src=get_file_path(os.getcwd(), output_dir, 'org-locations.js'),
dst=get_file_path(os.getcwd(), 'static', 'org-locations.js')
)

os.remove(get_file_path(os.getcwd(), output_dir, 'map.html'))


def move_leaflet_dist_folder(output_dir):
source_path = get_file_path(os.getcwd(), output_dir, 'leaflet_dist')
destination_path = get_file_path(os.getcwd(), 'static', 'leaflet_dist')

# Remove existing leaflet_dir if exists
for root, dirs, files in os.walk(destination_path):
for file in files:
os.remove(os.path.join(destination_path, file))
os.rmdir(root)

os.renames(source_path, destination_path)


def get_file_path(*args):
return '/'.join(args)
22 changes: 22 additions & 0 deletions data/tests/test_org_cluster_map_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
from django.test import TestCase

from data.models import Contributor
from data.org_cluster_map_handler import handle as org_cluster_map_handler


class CreateOrgClusterMapAndActivityGraphTest(TestCase):

@classmethod
def setUpTestData(cls):
Contributor.objects.create(login='test',
name='Test User',
location='{"latitude": 12.9,'
'"longitude": 77.8}')
Contributor.objects.create(login='testuser',
name='Test User 2')

def test_with_output_dir(self):
org_cluster_map_handler()

def test_without_output_dir(self):
org_cluster_map_handler(output_dir='org_map')
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
getorg~=0.3.1
git+https://gitlab.com/coala/coala-utils.git
git-url-parse
django>2.1,<2.2
Expand Down
Loading

0 comments on commit ae718af

Please sign in to comment.