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 Jun 16, 2019
1 parent 610f8d1 commit ec61b5e
Show file tree
Hide file tree
Showing 25 changed files with 961 additions and 190 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
1 change: 1 addition & 0 deletions .moban.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ packages:
- 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
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)
14 changes: 0 additions & 14 deletions community/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +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
Expand Down Expand Up @@ -87,18 +85,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 Down
98 changes: 90 additions & 8 deletions community/views.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,110 @@
from django.http import HttpResponse
from django.views.generic.base import TemplateView

import requests
import logging
from trav import Travis

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 = [
'{} newcomers'.format(org_name),
'{} developers'.format(org_name),
'{} admins'.format(org_name)
]
team_details = {}
for team_name in teams:
team = Team.objects.get(name=team_name)
team_details[team] = team.contributors.count()
return team_details

def get_quote_of_the_day(self):
qod = requests.get('http://quotes.rest/qod?category=inspire')
qod_data = qod.json()
try:
return {
'quote': qod_data['contents']['quotes'][0]['quote'],
'author': qod_data['contents']['quotes'][0]['author'],
}
except requests.HTTPError as err:
error_info = f'HTTPError while fetching Quote of the day! {err}'
print(error_info)
logging.error(error_info)
return None

print('Running on Travis: {}, build link: {}'.format(
context['isTravis'],
context['travisLink']))
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
4 changes: 1 addition & 3 deletions data/contrib_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,7 @@ def import_data(contributor):
try:
contributor['issues_opened'] = contributor.pop('issues')
contributor['num_commits'] = contributor.pop('contributions')
contributor_location = contributor.get('location')
if contributor_location:
contributor['location'] = contributor_location
contributor['location'] = contributor.get('location')
contributor.pop('teams')
c, create = Contributor.objects.get_or_create(
**contributor
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_20190615_1331.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# Generated by Django 2.1.7 on 2019-06-15 13:31

from django.db import migrations, models


class Migration(migrations.Migration):

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

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 @@ -16,7 +16,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
138 changes: 138 additions & 0 deletions data/org_cluster_map_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import json
import logging
import os
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
:return: None
"""
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.all():
if not contrib.login.startswith('testuser') and contrib.location:
user_location = json.loads(contrib.location)
location = Location(user_location['longitude'],
user_location['latitude'])
org_location_dict[contrib.login] = location
logger.debug('{} location {} added on map'.format(
contrib.login, user_location))
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 and
# the 'map.html' file to django templates directory to get it displayed
# on the homepage with the needed django syntax and CSS in the html file.
:param output_dir: Directory from where the files have to be moved
:return: None
"""

# Move leaflet_dist folder to static folder
leaflet_source_path = '{}/{}/leaflet_dist/'.format(os.getcwd(),
output_dir)
leaflet_destination_path = '{}/{}/leaflet_dist/'.format(os.getcwd(),
'static')

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

os.renames(leaflet_source_path, leaflet_destination_path)

# Move org_locations.js to static folder
locations_source_path = '{}/{}/org-locations.js'.format(os.getcwd(),
output_dir)
locations_destination_path = '{}/{}/org-locations.js'.format(
os.getcwd(), 'static')
os.rename(locations_source_path, locations_destination_path)

# Make changes in map.html to support django syntax with needed CSS
with open('{}/map.html'.format(output_dir)) as f:
django_supported_htmls = []
lines = f.readlines()
for index in range(len(lines)):
line = lines[index].strip()
if line.__contains__('<html>'):
django_supported_htmls.append('{% load staticfiles %}\n')
django_supported_htmls.append(line + '\n')
elif line.__contains__('</head>'):
adjust_prop = '''
<style>
#map {
width: 60%;
height: 300px;
margin: auto;
box-shadow: 0px 0px 25px 2px;
}
@media only screen and (max-width:750px){
#map {
width: 90%
}
}
</style>\n
'''
meta_charset = '<meta charset="utf-8">'
adjust_prop = adjust_prop.strip().replace(' ', '')
django_supported_htmls.insert(6, meta_charset + '\n')
django_supported_htmls.append(adjust_prop + '\n')
django_supported_htmls.append(line + '\n')
elif line.__contains__('https://'):
line = line.replace('https:', '').replace(' />', '>')
django_supported_htmls.append(line.strip() + '\n')
elif line.__contains__('<link '):
line = line.replace('href="', 'href="{% static \'')
line = line.replace('.css', '.css\' %}').replace(' />', '>')
django_supported_htmls.append(line + '\n')
elif line.__contains__('<script '):
line = line.replace('src="', 'src="{% static \'')
line = line.replace('.js', '.js\' %}')
if line.__contains__(' type="text/javascript"'):
line = line.replace(' type="text/javascript"', '')
django_supported_htmls.append(line + '\n')
elif line.__contains__('Mouse over') or len(line) == 0:
pass
else:
django_supported_htmls.append(line + '\n')

# Remove and Add 'map.html' to django templates directory
html_map_path = '{}/{}/map.html'
html_map_source_path = html_map_path.format(os.getcwd(),
output_dir)
html_map_destination_path = html_map_path.format(os.getcwd(),
'templates')
os.remove(html_map_source_path)
with open(html_map_destination_path, 'w+') as f:
f.writelines(django_supported_htmls)
Loading

0 comments on commit ec61b5e

Please sign in to comment.