Refactor view code
- Compress codebase - Remove duplicate bulky code - Use template directory to dictate sitemap
This commit is contained in:
parent
e2e4c517ec
commit
97601a7b53
32 changed files with 96 additions and 313 deletions
1
data/context.yml
Normal file
1
data/context.yml
Normal file
|
@ -0,0 +1 @@
|
||||||
|
|
9
data/page_context.yml
Normal file
9
data/page_context.yml
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
index:
|
||||||
|
body_class: index
|
||||||
|
html_title: Homepage
|
||||||
|
|
||||||
|
projects/attack-on-blocks:
|
||||||
|
page_title: Attack On Block Game!
|
||||||
|
|
||||||
|
projects/hipchat-emoticons-for-all:
|
||||||
|
header_image: https://hipchat-magnolia-cdn.atlassian.com/assets/img/hipchat/hipchat_og_image.jpg
|
|
@ -1,10 +0,0 @@
|
||||||
{
|
|
||||||
"hipchat-emoticons-for-all": {
|
|
||||||
"title": "Hipchat Emoticons Plugin",
|
|
||||||
"image": "https://hipchat-magnolia-cdn.atlassian.com/assets/img/hipchat/hipchat_og_image.jpg"
|
|
||||||
},
|
|
||||||
"attack-on-blocks": {
|
|
||||||
"title": "Attack on Blocks Game",
|
|
||||||
"image": "https://image.freepik.com/free-vector/space-invaders-game_62147502273.jpg"
|
|
||||||
}
|
|
||||||
}
|
|
8
project/common/data.py
Normal file
8
project/common/data.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
import os.path
|
||||||
|
import yaml
|
||||||
|
|
||||||
|
|
||||||
|
def generate_config(base_dir):
|
||||||
|
default = yaml.load(open(os.path.join(base_dir, 'data/context.yml'))) or {}
|
||||||
|
page = yaml.load(open(os.path.join(base_dir, 'data/page_context.yml'))) or {}
|
||||||
|
return default, page
|
|
@ -1,105 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
from django.core.urlresolvers import reverse
|
|
||||||
from django.conf import settings
|
|
||||||
import os.path, json
|
|
||||||
|
|
||||||
|
|
||||||
class AboutTestCase(TestCase):
|
|
||||||
def test_website_accessable(self):
|
|
||||||
response = self.client.get(reverse('about:website'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_me_accessable(self):
|
|
||||||
response = self.client.get(reverse('about:me'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_index_accessable(self):
|
|
||||||
response = self.client.get(reverse('about:index'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_email_send(self):
|
|
||||||
data = {
|
|
||||||
'email': '123@123.123',
|
|
||||||
'name': 'Person',
|
|
||||||
'message': 'Hi there, things.'
|
|
||||||
}
|
|
||||||
response = self.client.post(reverse('about:index'), data)
|
|
||||||
self.assertRedirects(response, '/about/?sent')
|
|
||||||
|
|
||||||
def test_success_message_shows(self):
|
|
||||||
response = self.client.get(reverse('about:index') + '?sent')
|
|
||||||
self.assertContains(response, 'Already Sent')
|
|
||||||
|
|
||||||
|
|
||||||
class CorePagesTestCase(TestCase):
|
|
||||||
def test_404_accessable(self):
|
|
||||||
response = self.client.get(reverse('404'))
|
|
||||||
self.assertEqual(response.status_code, 404)
|
|
||||||
|
|
||||||
def test_no_js_accessable(self):
|
|
||||||
response = self.client.get(reverse('no-js'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_index_accessable(self):
|
|
||||||
response = self.client.get(reverse('pages:index'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_index_projects(self):
|
|
||||||
projects = json.load(open(os.path.join(settings.BASE_DIR, 'data/projects.json')))
|
|
||||||
response = self.client.get(reverse('pages:index'))
|
|
||||||
for key, project in projects.items():
|
|
||||||
self.assertContains(response, project['title'])
|
|
||||||
if 'image' in project:
|
|
||||||
self.assertContains(response, project['image'])
|
|
||||||
|
|
||||||
|
|
||||||
class ProjectsTestCase(TestCase):
|
|
||||||
def test_all_accessable(self):
|
|
||||||
response = self.client.get(reverse('projects:all'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_test_project_accessable(self):
|
|
||||||
response = self.client.get(reverse('projects:project', args=['test']))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_projects_accessable(self):
|
|
||||||
projects = json.load(open(os.path.join(settings.BASE_DIR, 'data/projects.json')))
|
|
||||||
for key, project in projects.items():
|
|
||||||
response = self.client.get(reverse('projects:project', args=[key]))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_projects_details(self):
|
|
||||||
projects = json.load(open(os.path.join(settings.BASE_DIR, 'data/projects.json')))
|
|
||||||
for key, project in projects.items():
|
|
||||||
response = self.client.get(reverse('projects:project', args=[key]))
|
|
||||||
self.assertContains(response, project['title'])
|
|
||||||
if 'image' in project:
|
|
||||||
self.assertContains(response, project['image'])
|
|
||||||
|
|
||||||
|
|
||||||
class RoboticsTestCase(TestCase):
|
|
||||||
def test_2015_index_accessable(self):
|
|
||||||
response = self.client.get(reverse('robotics:2015-index'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_2014_index_accessable(self):
|
|
||||||
response = self.client.get(reverse('robotics:2014-index'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_2015_robot_accessable(self):
|
|
||||||
response = self.client.get(reverse('robotics:2015-robot'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_2015_code_accessable(self):
|
|
||||||
response = self.client.get(reverse('robotics:2015-code'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
|
|
||||||
class CollegeTestCase(TestCase):
|
|
||||||
def test_attack_on_blocks_accessable(self):
|
|
||||||
response = self.client.get(reverse('college:attack-on-blocks'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
||||||
|
|
||||||
def test_student_server_accessable(self):
|
|
||||||
response = self.client.get(reverse('college:student-server'))
|
|
||||||
self.assertEqual(response.status_code, 200)
|
|
8
project/pages/urls.py
Normal file
8
project/pages/urls.py
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
from django.conf.urls import include, url
|
||||||
|
from .views import page_view, index_view
|
||||||
|
|
||||||
|
|
||||||
|
urlpatterns = [
|
||||||
|
url(r'^(?P<path>.*)/', page_view, name='page'),
|
||||||
|
url(r'^$', index_view, name='page'),
|
||||||
|
]
|
|
@ -1 +0,0 @@
|
||||||
from . import about, college, core, projects, robotics
|
|
|
@ -1,9 +0,0 @@
|
||||||
from django.conf.urls import url
|
|
||||||
from project.pages.views import about
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
url(r'^website/$', about.WebsiteView.as_view(), name='website'),
|
|
||||||
url(r'^me/$', about.MeView.as_view(), name='me'),
|
|
||||||
url(r'^$', about.IndexView.as_view(), name='index'),
|
|
||||||
]
|
|
|
@ -1,8 +0,0 @@
|
||||||
from django.conf.urls import url
|
|
||||||
from project.pages.views import college
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
url(r'^attack-on-blocks/$', college.AttackOnBlocksView.as_view(), name='attack-on-blocks'),
|
|
||||||
url(r'^student-server/$', college.StudentServerView.as_view(), name='student-server'),
|
|
||||||
]
|
|
|
@ -1,7 +0,0 @@
|
||||||
from django.conf.urls import url
|
|
||||||
from project.pages.views import core
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
url(r'^$', core.IndexView.as_view(), name='index')
|
|
||||||
]
|
|
|
@ -1,8 +0,0 @@
|
||||||
from django.conf.urls import url
|
|
||||||
from project.pages.views import projects
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
url(r'^$', projects.AllView.as_view(), name="all"),
|
|
||||||
url(r'^(?P<project>.+)/$', projects.ProjectView.as_view(), name="project")
|
|
||||||
]
|
|
|
@ -1,11 +0,0 @@
|
||||||
from django.conf.urls import url
|
|
||||||
from project.pages.views import robotics
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
|
||||||
url(r'^$', robotics.IndexView.as_view(), name="index"),
|
|
||||||
url(r'^2014/$', robotics.Index2014View.as_view(), name="2014-index"),
|
|
||||||
url(r'^2015/robot/$', robotics.Robot2015View.as_view(), name="2015-robot"),
|
|
||||||
url(r'^2015/code/$', robotics.Code2015View.as_view(), name="2015-code"),
|
|
||||||
url(r'^2015/$', robotics.Index2015View.as_view(), name="2015-index")
|
|
||||||
]
|
|
23
project/pages/utils.py
Normal file
23
project/pages/utils.py
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
from django.conf import settings
|
||||||
|
from bs4 import BeautifulSoup
|
||||||
|
import markdown2
|
||||||
|
|
||||||
|
|
||||||
|
def get_context(path):
|
||||||
|
if path in settings.PAGE_CONTEXT:
|
||||||
|
context = dict(settings.DEFAULT_CONTEXT, **settings.PAGE_CONTEXT[path])
|
||||||
|
else:
|
||||||
|
context = dict(settings.DEFAULT_CONTEXT)
|
||||||
|
return context
|
||||||
|
|
||||||
|
|
||||||
|
def get_title_from_markdown(md):
|
||||||
|
html_tree = BeautifulSoup(md, "html.parser")
|
||||||
|
tag = html_tree.find('h1')
|
||||||
|
return tag.contents[0]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_content(content, extension):
|
||||||
|
if extension == 'md':
|
||||||
|
return markdown2.markdown(content)
|
||||||
|
return content
|
32
project/pages/views.py
Normal file
32
project/pages/views.py
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
from project.common.views import CustomTemplate
|
||||||
|
import os.path
|
||||||
|
from django.conf import settings
|
||||||
|
from django.http import HttpResponse, Http404
|
||||||
|
from django.template.loader import get_template
|
||||||
|
from .utils import get_context, parse_content, get_title_from_markdown
|
||||||
|
|
||||||
|
|
||||||
|
def page_view(request, path):
|
||||||
|
template = None
|
||||||
|
if os.path.isdir(os.path.join(settings.BASE_DIR, 'templates', path)):
|
||||||
|
path = os.path.join(path, 'index')
|
||||||
|
for extension in ['md', 'html']:
|
||||||
|
try:
|
||||||
|
template = get_template("{}.{}".format(path, extension))
|
||||||
|
break
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if not template:
|
||||||
|
raise Http404
|
||||||
|
context = get_context(path)
|
||||||
|
parsed_content = parse_content(template.render(context, request), extension)
|
||||||
|
if extension == 'md':
|
||||||
|
template = get_template('markdown_content.html')
|
||||||
|
context['markdown_content'] = parsed_content
|
||||||
|
context['page_title'] = get_title_from_markdown(parsed_content)
|
||||||
|
parsed_content = template.render(context, request)
|
||||||
|
return HttpResponse(parsed_content)
|
||||||
|
|
||||||
|
|
||||||
|
def index_view(request):
|
||||||
|
return page_view(request, 'index')
|
|
@ -1 +0,0 @@
|
||||||
from . import about, college, core, projects, robotics
|
|
|
@ -1,28 +0,0 @@
|
||||||
from project.common.views import CustomTemplate, CustomFormTemplate
|
|
||||||
from project.common.forms import ContactForm
|
|
||||||
|
|
||||||
|
|
||||||
class WebsiteView(CustomTemplate):
|
|
||||||
template_name = 'about/website.html'
|
|
||||||
html_title = "About website"
|
|
||||||
|
|
||||||
|
|
||||||
class IndexView(CustomFormTemplate):
|
|
||||||
template_name = 'about/index.html'
|
|
||||||
html_title = "About"
|
|
||||||
success_url = '/about/?sent'
|
|
||||||
form_class = ContactForm
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['sent'] = 'sent' not in self.request.GET
|
|
||||||
return context
|
|
||||||
|
|
||||||
def form_valid(self, form):
|
|
||||||
form.send_email()
|
|
||||||
return super().form_valid(form)
|
|
||||||
|
|
||||||
|
|
||||||
class MeView(CustomTemplate):
|
|
||||||
template_name = 'about/me.html'
|
|
||||||
html_title = "About Me"
|
|
|
@ -1,9 +0,0 @@
|
||||||
from project.common.views import MarkdownView
|
|
||||||
|
|
||||||
|
|
||||||
class AttackOnBlocksView(MarkdownView):
|
|
||||||
markdown = 'projects/attack-on-blocks.md'
|
|
||||||
|
|
||||||
|
|
||||||
class StudentServerView(MarkdownView):
|
|
||||||
markdown = 'college/student-server.md'
|
|
|
@ -1,32 +0,0 @@
|
||||||
from project.common.views import CustomTemplate
|
|
||||||
from django.conf import settings
|
|
||||||
from json import load
|
|
||||||
import os.path
|
|
||||||
|
|
||||||
|
|
||||||
class IndexView(CustomTemplate):
|
|
||||||
template_name = 'index.html'
|
|
||||||
html_title = "Homepage"
|
|
||||||
body_class = "index"
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['projects'] = load(open(os.path.join(settings.BASE_DIR, 'data/projects.json')))
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class NoJavascriptView(CustomTemplate):
|
|
||||||
template_name = 'core/no-js.html'
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['js_redirect'] = False
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class Custom404View(CustomTemplate):
|
|
||||||
template_name = 'core/404.html'
|
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
|
||||||
context = self.get_context_data(**kwargs)
|
|
||||||
return self.render_to_response(context, status=404)
|
|
|
@ -1,34 +0,0 @@
|
||||||
from project.common.views import CustomTemplate, MarkdownView
|
|
||||||
from json import load
|
|
||||||
from django.conf import settings
|
|
||||||
import os.path
|
|
||||||
|
|
||||||
|
|
||||||
PROJECT_DETAILS = load(open(os.path.join(settings.BASE_DIR, 'data/projects.json')))
|
|
||||||
|
|
||||||
|
|
||||||
class AllView(CustomTemplate):
|
|
||||||
template_name = 'projects/all.html'
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
context['projects'] = PROJECT_DETAILS
|
|
||||||
return context
|
|
||||||
|
|
||||||
|
|
||||||
class ProjectView(MarkdownView):
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
context = super().get_context_data(**kwargs)
|
|
||||||
try:
|
|
||||||
details = PROJECT_DETAILS[kwargs['project']]
|
|
||||||
context['html_title'] = details['title']
|
|
||||||
context['page_title'] = details['title']
|
|
||||||
context['header_image'] = details['image']
|
|
||||||
except:
|
|
||||||
context['html_title'] = kwargs['project']
|
|
||||||
context['page_title'] = kwargs['project']
|
|
||||||
return context
|
|
||||||
|
|
||||||
def dispatch(self, request, *args, **kwargs):
|
|
||||||
self.markdown = 'projects/{0}.md'.format(kwargs['project'])
|
|
||||||
return super().dispatch(request, *args, **kwargs)
|
|
|
@ -1,27 +0,0 @@
|
||||||
from project.common.views import CustomTemplate, MarkdownView
|
|
||||||
|
|
||||||
|
|
||||||
class IndexView(CustomTemplate):
|
|
||||||
template_name = 'robotics/index.html'
|
|
||||||
html_title = 'Student Robotics'
|
|
||||||
|
|
||||||
|
|
||||||
# 2015
|
|
||||||
class Index2015View(CustomTemplate):
|
|
||||||
template_name = 'robotics/2015-index.html'
|
|
||||||
html_title = 'Student Robotics 2015'
|
|
||||||
|
|
||||||
|
|
||||||
class Robot2015View(CustomTemplate):
|
|
||||||
template_name = 'robotics/2015-robot.html'
|
|
||||||
html_title = 'The Robot | SR2015'
|
|
||||||
|
|
||||||
|
|
||||||
class Code2015View(MarkdownView):
|
|
||||||
markdown = 'robotics/2015-code.md'
|
|
||||||
|
|
||||||
|
|
||||||
# 2014
|
|
||||||
class Index2014View(CustomTemplate):
|
|
||||||
template_name = 'robotics/2014-index.html'
|
|
||||||
html_title = 'Student Robotics 2014'
|
|
|
@ -96,3 +96,7 @@ JOBS = {
|
||||||
}
|
}
|
||||||
|
|
||||||
WORDPRESS_URL = "realorangeone.wordpress.com"
|
WORDPRESS_URL = "realorangeone.wordpress.com"
|
||||||
|
|
||||||
|
# Generate config data
|
||||||
|
from project.common.data import generate_config
|
||||||
|
DEFAULT_CONTEXT, PAGE_CONTEXT = generate_config(BASE_DIR)
|
||||||
|
|
|
@ -1,19 +1,7 @@
|
||||||
from django.conf.urls import include, url
|
from django.conf.urls import include, url
|
||||||
from django_client_reverse import urls as reverse_urls
|
|
||||||
from project.pages.views.core import Custom404View, NoJavascriptView
|
|
||||||
|
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
url(r'^reverse/$', include(reverse_urls, namespace='reverser')),
|
|
||||||
url(r'^404/', Custom404View.as_view(), name="404"),
|
|
||||||
url(r'^no-js/', NoJavascriptView.as_view(), name="no-js"),
|
|
||||||
url(r'^about/', include('project.pages.urls.about', namespace='about')),
|
|
||||||
url(r'^college/', include('project.pages.urls.college', namespace='college')),
|
|
||||||
url(r'^core/', include('project.pages.urls.core', namespace='core')),
|
|
||||||
url(r'^projects/', include('project.pages.urls.projects', namespace='projects')),
|
|
||||||
url(r'^robotics/', include('project.pages.urls.robotics', namespace='robotics')),
|
|
||||||
url(r'^blog/', include('project.blog.urls', namespace='blog')),
|
url(r'^blog/', include('project.blog.urls', namespace='blog')),
|
||||||
url(r'', include('project.pages.urls.core', namespace='pages'))
|
url(r'', include('project.pages.urls', namespace='pages'))
|
||||||
]
|
]
|
||||||
|
|
||||||
# handler404 = Custom404View
|
|
||||||
|
|
|
@ -1,10 +1,10 @@
|
||||||
|
beautifulsoup4==4.3.2
|
||||||
coverage==4.0.3
|
coverage==4.0.3
|
||||||
colorama==0.3.6
|
colorama==0.3.6
|
||||||
Django==1.8.7
|
Django==1.8.7
|
||||||
dj-database-url==0.3.0
|
dj-database-url==0.3.0
|
||||||
django-db-queue==0.0.2
|
django-db-queue==0.0.2
|
||||||
django-bootstrap-form==3.2
|
django-bootstrap-form==3.2
|
||||||
git+https://github.com/RealOrangeOne/django-client-reverse
|
|
||||||
django-flat-theme==1.1.3
|
django-flat-theme==1.1.3
|
||||||
djangorestframework==3.3.2
|
djangorestframework==3.3.2
|
||||||
flake8==2.5.0
|
flake8==2.5.0
|
||||||
|
|
|
@ -6,14 +6,10 @@ import './globals.js';
|
||||||
import 'whatwg-fetch';
|
import 'whatwg-fetch';
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import ProjectImagesTypes from './components/index/project-images-types';
|
|
||||||
|
|
||||||
import NavBar from './components/navbar/navbar';
|
import NavBar from './components/navbar/navbar';
|
||||||
import Breadcrumbs from './components/breadcrumbs';
|
import Breadcrumbs from './components/breadcrumbs';
|
||||||
|
|
||||||
if ($('body').hasClass('index')) { // Render components on index
|
|
||||||
React.render(<ProjectImagesTypes />, document.getElementById('project-images-types'));
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($('navbar').length > 0) {
|
if ($('navbar').length > 0) {
|
||||||
React.render(<NavBar />, $('navbar')[0]);
|
React.render(<NavBar />, $('navbar')[0]);
|
||||||
|
@ -22,3 +18,7 @@ if ($('navbar').length > 0) {
|
||||||
if ($('#breadcrumbs').length > 0) {
|
if ($('#breadcrumbs').length > 0) {
|
||||||
React.render(<Breadcrumbs />, $('#breadcrumbs')[0]);
|
React.render(<Breadcrumbs />, $('#breadcrumbs')[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($('.header h1').text()) {
|
||||||
|
$('.markdown-content h1').eq(0).hide();
|
||||||
|
}
|
||||||
|
|
|
@ -57,7 +57,7 @@
|
||||||
{% if js_redirect %}
|
{% if js_redirect %}
|
||||||
<noscript>
|
<noscript>
|
||||||
<style> html, body { display:none; }</style>
|
<style> html, body { display:none; }</style>
|
||||||
<meta http-equiv="refresh" content="0.0;url={% url 'no-js' %}">
|
<meta http-equiv="refresh" content="0.0;url=/no-js">
|
||||||
</noscript>
|
</noscript>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</body>
|
</body>
|
||||||
|
|
|
@ -44,7 +44,7 @@
|
||||||
<div class="caption">
|
<div class="caption">
|
||||||
<h3>{{ project.title }}</h3>
|
<h3>{{ project.title }}</h3>
|
||||||
<p></p>
|
<p></p>
|
||||||
<a href="{% url 'projects:project' key %}">
|
<a href="/project/">
|
||||||
Read More
|
Read More
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -53,7 +53,7 @@
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
<div class="col-sm-6 col-md-4">
|
<div class="col-sm-6 col-md-4">
|
||||||
<div class="thumbnail see-more">
|
<div class="thumbnail see-more">
|
||||||
<a href="{% url 'projects:all' %}">
|
<a href="/projects">
|
||||||
<h1>See All</h1>
|
<h1>See All</h1>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
<h2>Student Robotics 2014</h2>
|
<h2>Student Robotics 2014</h2>
|
||||||
<p><strong>Robot Name:</strong> Lucy
|
<p><strong>Robot Name:</strong> Lucy
|
||||||
<br /><small>(No, it doesn't stand for anything)</small>
|
<br /><small>(No, it doesn't stand for anything)</small>
|
||||||
<p><a href="{% url 'robotics:2014-index' %}" class="btn btn-srobo btn-block">More Info</a></p>
|
<p><a href="/robotics/2014" class="btn btn-srobo btn-block">More Info</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
<h2>Student Robotics 2015</h2>
|
<h2>Student Robotics 2015</h2>
|
||||||
<p><strong>Robot Name : </strong> A.L.I.C.E
|
<p><strong>Robot Name : </strong> A.L.I.C.E
|
||||||
<br /><small>(<strong>A</strong>utonomous <strong>L</strong>ogistics and <strong>I</strong>nevitable <strong>C</strong>ollision <strong>E</strong>ngine)</small></p>
|
<br /><small>(<strong>A</strong>utonomous <strong>L</strong>ogistics and <strong>I</strong>nevitable <strong>C</strong>ollision <strong>E</strong>ngine)</small></p>
|
||||||
<p><a href="{% url 'robotics:2015-index' %}" class="btn btn-srobo btn-block" >More Info</a></p>
|
<p><a href="/robotics/2015" class="btn btn-srobo btn-block" >More Info</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Reference in a new issue