diff --git a/.ci/build.sh b/.ci/build.sh index 1f01164d..56c2be06 100755 --- a/.ci/build.sh +++ b/.ci/build.sh @@ -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 diff --git a/.coafile b/.coafile index 8dc3cf12..3b4c1c5b 100644 --- a/.coafile +++ b/.coafile @@ -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 = ' @@ -42,6 +42,7 @@ files = static/**/*.js bears = JSHintBear allow_unused_variables = True javascript_strictness = False +environment_jquery = True [all.yml] bears = YAMLLintBear diff --git a/.moban.yaml b/.moban.yaml index 574c2d69..5bf25cb1 100644 --- a/.moban.yaml +++ b/.moban.yaml @@ -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 diff --git a/.nocover.yaml b/.nocover.yaml index 987773ce..b2207eb5 100644 --- a/.nocover.yaml +++ b/.nocover.yaml @@ -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 diff --git a/activity/scraper.py b/activity/scraper.py index 069bc668..9d8fc794 100644 --- a/activity/scraper.py +++ b/activity/scraper.py @@ -136,7 +136,7 @@ def get_data(self): return self.data -def activity_json(request): +def activity_json(filename): org_name = get_org_name() @@ -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) diff --git a/community/urls.py b/community/urls.py index ed936b9d..cb37e8e2 100644 --- a/community/urls.py +++ b/community/urls.py @@ -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 @@ -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', @@ -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', diff --git a/community/views.py b/community/views.py index 595c02ed..5c1bbb2f 100644 --- a/community/views.py +++ b/community/views.py @@ -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 diff --git a/data/management/commands/create_org_cluster_map_and_activity_graph.py b/data/management/commands/create_org_cluster_map_and_activity_graph.py new file mode 100644 index 00000000..c71647b1 --- /dev/null +++ b/data/management/commands/create_org_cluster_map_and_activity_graph.py @@ -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') diff --git a/data/migrations/0006_auto_20190801_1752.py b/data/migrations/0006_auto_20190801_1752.py new file mode 100644 index 00000000..aa2d0ef6 --- /dev/null +++ b/data/migrations/0006_auto_20190801_1752.py @@ -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'), + ), + ] diff --git a/data/models.py b/data/models.py index ed3b854b..02050fcb 100644 --- a/data/models.py +++ b/data/models.py @@ -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 diff --git a/data/org_cluster_map_handler.py b/data/org_cluster_map_handler.py new file mode 100644 index 00000000..baf0969b --- /dev/null +++ b/data/org_cluster_map_handler.py @@ -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) diff --git a/data/tests/test_org_cluster_map_handler.py b/data/tests/test_org_cluster_map_handler.py new file mode 100644 index 00000000..8199dcc0 --- /dev/null +++ b/data/tests/test_org_cluster_map_handler.py @@ -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') diff --git a/requirements.txt b/requirements.txt index d2f46479..5c915a34 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,3 +1,4 @@ +getorg~=0.3.1 git+https://gitlab.com/coala/coala-utils.git git-url-parse django>2.1,<2.2 diff --git a/setup.cfg b/setup.cfg index 4087933a..a3f699bf 100644 --- a/setup.cfg +++ b/setup.cfg @@ -17,7 +17,6 @@ testpaths = log meta_review model - twitter unassigned_issues python_files = test_*.py @@ -73,7 +72,6 @@ source = log meta_review model - twitter unassigned_issues omit = @@ -86,7 +84,6 @@ omit = meta_review/handler.py model/*.py openhub/*.py - twitter/*.py inactive_issues/inactive_issues_scraper.py unassigned_issues/unassigned_issues_scraper.py */migrations/*.py diff --git a/static/charts.js b/static/charts.js deleted file mode 100644 index 6169129e..00000000 --- a/static/charts.js +++ /dev/null @@ -1,86 +0,0 @@ -/* globals $, Chart */ -var curChart; - -function setChart(labels, openedData, closedData, type) { - var ctx = document.getElementById("canvas"); - - curChart = new Chart(ctx, { - type: 'line', - data: { - labels: labels, - datasets: [{ - label: "Issues Opened", - backgroundColor: "RGBA(33, 150, 243, 0.2)", - borderColor: "RGBA(33, 150, 243, 1)", - data: openedData, - fill: true, - }, { - label: "Issues Closed", - backgroundColor: "RGBA(244, 67, 54, 0.2)", - borderColor: "RGBA(244, 67, 54, 1)", - data: closedData, - fill: true, - }] - }, - options: { - responsive: true, - title: { - display: true, - text: 'Community Activity' - }, - tooltips: { - mode: 'index', - intersect: false, - }, - hover: { - mode: 'nearest', - intersect: true - }, - scales: { - xAxes: [{ - display: true, - scaleLabel: { - display: true, - labelString: type - } - }], - yAxes: [{ - display: true, - scaleLabel: { - display: true, - labelString: 'Number' - } - }] - } - } - }); -} - -function updateChart(type) { - if(curChart){ curChart.destroy(); } - - $.getJSON("/static/activity-data.json", - function(data) { - var labels, openedData, closedData; - if(type === "Month") { - labels = data.year.labels; - openedData = data.year.opened; - closedData = data.year.closed; - } - else if(type === "Week") { - labels = data.month.labels; - openedData = data.month.opened; - closedData = data.month.closed; - } - else { - labels = data.week.labels; - openedData = data.week.opened; - closedData = data.week.closed; - } - setChart(labels, openedData, closedData, type); - }) - .fail(function(data, textStatus, error) { - var err = textStatus + ", " + error; - console.error("Request Failed: " + err); - }); -} diff --git a/static/css/index.css b/static/css/index.css new file mode 100644 index 00000000..d50f0193 --- /dev/null +++ b/static/css/index.css @@ -0,0 +1,114 @@ +.activity-graph { + display: flex; + justify-content: center; + flex-direction: column; + align-items: center; + padding-top: 3%; +} + +.activity-graph-canvas { + width: 75%; +} + +.activity-graph .input-field { + width: 100px; + margin-top: 0; +} + +.community-header-image img { + width: 100%; + box-shadow: 0 -5px 15px black; +} + +.join-btn a.btn-large { + min-width: 300px; + width: 30%; + margin-top: 3%; + box-shadow: 0 0 25px 2px black; + border-radius: 100px; + background-color: #edf5af; + color: #636060; + text-transform: full-width; + font-size: 1em; + letter-spacing: 0; + padding: 0 3%; +} + +.leaderboards-btn { + float: right; + width: 100%; + border-bottom-right-radius: 1000px; + border-bottom-left-radius: 1000px; +} + +.org-teams-stats { + width: 70%; + margin: auto; +} + +.organization_map { + text-align: center; + padding: 2% 0; +} + +.top-meta-reviewers { + background-color: #fef200; +} + +.top-gamifiers { + background-color: #ee8940; +} + +.top-contributors { + -webkit-flex: 1; + -ms-flex: 1; + flex: 1; + display: flex; + justify-content: center; + flex-flow: row wrap; + padding-top: 2%; +} + +.top-meta-reviewers, +.top-gamifiers { + margin: 0 3%; + border-radius: 10%; + box-shadow: 0 0 25px 2px; + font-size: medium; +} + +.quote-of-day div.quote{ + width: auto; + max-width: 70%; +} + +@media only screen and (min-width: 858px) { + .community-header-image img{ + height: 450px; + + } +} + +@media only screen and (max-width: 600px) { + .activity-graph { + display: none; + } + .join-btn a.btn-large { + margin-bottom: 20px; + } +} + +@media only screen and (min-width: 900px){ + .top-meta-reviewers, + .top-gamifiers { + width: 35%; + } +} + +@media only screen and (max-width: 644px){ + .top-meta-reviewers, + .top-gamifiers { + margin: 3% 3%; + width: 70% + } +} diff --git a/static/css/main.css b/static/css/main.css new file mode 100644 index 00000000..af72908b --- /dev/null +++ b/static/css/main.css @@ -0,0 +1,169 @@ +.apply-flex { + display: flex; + flex-flow: row wrap; + align-items: center; +} + +body { + font-family: 'Ubuntu Mono', monospace; + background-color: #edf5af; +} + +.bold-text, +.sidenav.bold-text { + font-weight: bold; +} + +.bold-text i.social-icons, +.sidenav i.social-icons { + font-size: 1.5em; +} + +.clickable-row { + cursor: pointer; +} + +.custom-green-color-font { + color: #2d5d13 +} + +.center-content { + justify-content: center; +} + +.evenly-spread-content { + justify-content: space-evenly; +} + +.gray-font-color { + color: #37474f; +} + +footer .footer-icons { + display: flex; + flex-wrap: wrap; + justify-content: space-evenly; + line-height: 3em; +} + +footer.footer-icons.icon { + color: white; +} + +footer .social-buttons { + background-color: black; + text-transform: none; +} + +.large-font { + font-size: larger; +} + +.main-content { + background-color: #edf5af; + padding-bottom: 3%; +} + +.main-content h3 { + color: #37474f; +} + +nav { + background-color: #37474f; +} + +nav .brand-logo img { + width: 60px; +} + +.nav-menu-font-size, +.sidenav .nav-menu-font-size { + font-size: 20px; +} +p { + font-size: medium; +} + +.page-name { + align-items: center; + display: flex; + text-align: center; +} + +.page-footer { + background-color: #37474f; + box-shadow: 0 -5px 15px black; +} + +.page-footer .footer-copyright { + background-color: #263238; +} + +.inline-contents { + display: inline-flex; +} + +.search-field { + border-radius: 100px; + box-shadow: 0 0 25px 2px black; + color: #454343; + background-color: #edf5af; + padding: 0 20px; + flex-flow: row; + margin: auto auto auto 0; +} + +#search { + border-bottom: none; + padding-left: 15px; + margin-bottom: 0; + font-weight: bold; +} + +#search::placeholder { + color: grey; +} + +#search:focus:not(.browser-default) { + background-color: #edf5af; + border-radius: 100px; + font-size: medium; + padding-left: 15px; + margin-bottom: 0; +} + +.sidenav .dropdown-trigger-sidenav i { + float: none; +} + +.student { + padding-bottom: 20px; +} + +.students { + list-style: none; +} + +.web-page-details { + width: 100%; +} + +.web-page-description, +.data-updated-time, +.data-fetch-error { + text-align: center; + font-size: large; + color: #2d5d13; +} + +@media only screen and (max-width: 600px) { + nav .brand-logo img { + width: 55px; + } +} + +@media only screen and (max-width: 530px){ + .page-name { + margin: 0; + } +} diff --git a/static/images/favicon.ico b/static/images/favicon.ico new file mode 100644 index 00000000..8290d1ba Binary files /dev/null and b/static/images/favicon.ico differ diff --git a/static/images/os_community_header.jpg b/static/images/os_community_header.jpg new file mode 100644 index 00000000..82d28ecb Binary files /dev/null and b/static/images/os_community_header.jpg differ diff --git a/static/images/os_community_logo.png b/static/images/os_community_logo.png new file mode 100644 index 00000000..3b2b2da0 Binary files /dev/null and b/static/images/os_community_logo.png differ diff --git a/static/js/index.js b/static/js/index.js new file mode 100644 index 00000000..f8a9383a --- /dev/null +++ b/static/js/index.js @@ -0,0 +1,99 @@ +/* globals Chart */ +$(document).ready(function(){ + $(".clickable-row").click(function() { + window.location = $(this).data("href"); + }); + + var chart_type_selector = $('#chart-type'); + var curChart; + + function setChart(labels, openedData, closedData, type) { + var ctx = document.getElementById("canvas"); + + curChart = new Chart(ctx, { + type: 'line', + data: { + labels: labels, + datasets: [{ + label: "Issues Opened", + backgroundColor: "RGBA(33, 150, 243, 0.2)", + borderColor: "RGBA(33, 150, 243, 1)", + data: openedData, + fill: true, + }, { + label: "Issues Closed", + backgroundColor: "RGBA(244, 67, 54, 0.2)", + borderColor: "RGBA(244, 67, 54, 1)", + data: closedData, + fill: true, + }] + }, + options: { + responsive: true, + title: { + display: true, + text: 'Community Activity' + }, + tooltips: { + mode: 'index', + intersect: false, + }, + hover: { + mode: 'nearest', + intersect: true + }, + scales: { + xAxes: [{ + display: true, + scaleLabel: { + display: true, + labelString: type + } + }], + yAxes: [{ + display: true, + scaleLabel: { + display: true, + labelString: 'Number' + } + }] + } + } + }); + } + + function updateChart(type) { + if(curChart){ curChart.destroy(); } + + $.getJSON("/static/activity-data.js", + function(data) { + var labels, openedData, closedData; + if(type === "Month") { + labels = data.year.labels; + openedData = data.year.opened; + closedData = data.year.closed; + } + else if(type === "Week") { + labels = data.month.labels; + openedData = data.month.opened; + closedData = data.month.closed; + } + else { + labels = data.week.labels; + openedData = data.week.opened; + closedData = data.week.closed; + } + setChart(labels, openedData, closedData, type); + }) + .fail(function(data, textStatus, error) { + var err = textStatus + ", " + error; + console.error("Request Failed: " + err); + }); + } + + chart_type_selector.on('change', function(){ + updateChart($('#chart-type').val()); + }); + + updateChart(chart_type_selector.val()); +}); diff --git a/static/js/main.js b/static/js/main.js new file mode 100644 index 00000000..f01f9eb4 --- /dev/null +++ b/static/js/main.js @@ -0,0 +1,23 @@ +$(document).ready(function(){ + function activate_dropdown(){ + if ($('nav').width() < 992 ){ + $(".dropdown-trigger-sidenav").dropdown({coverTrigger: false}); + } + else { + $(".dropdown-trigger").dropdown({hover: true, + constrainWidth: false, + coverTrigger: false}); + } + } + + activate_dropdown(); + + $('.sidenav').sidenav(); + $('select').formSelect(); + + $(window).resize(function(){ + activate_dropdown(); + }); + + $('#current-year').html(new Date().getFullYear()); +}); diff --git a/static/main.css b/static/main.css deleted file mode 100644 index d2f410e3..00000000 --- a/static/main.css +++ /dev/null @@ -1,7 +0,0 @@ -.student { - padding-bottom: 20px; -} - -.students { - list-style: none; -} diff --git a/templates/activity.html b/templates/activity.html deleted file mode 100644 index a16da76f..00000000 --- a/templates/activity.html +++ /dev/null @@ -1,39 +0,0 @@ - - -
-{{ org.about }}
+{{ quote_details.quote }}
+Rank | +Username | +Gamification Score | +
---|---|---|
{{ index|add:"1" }} | +{{ contrib.username }} | +{{ contrib.score }} | +