Add Bootstrap CSS, management script to load locale data, add new fields to locale models

This commit is contained in:
Michael Hall 2018-01-03 23:44:27 -05:00
parent d70435fdcc
commit f4cf1c9cda
21 changed files with 335 additions and 19 deletions

View file

@ -12,14 +12,21 @@ admin.site.register(Country)
class SPRAdmin(admin.ModelAdmin):
raw_id_fields = ('country',)
list_filter =('country',)
search_fields = ('name', 'country__name')
admin.site.register(SPR, SPRAdmin)
class CityAdmin(admin.ModelAdmin):
raw_id_fields = ('spr',)
list_filter =('spr__country',)
search_fields = ('name', 'spr__name')
admin.site.register(City, CityAdmin)
admin.site.register(UserProfile)
admin.site.register(Organization)
class OrgAdmin(admin.ModelAdmin):
list_display = ('name', 'site')
admin.site.register(Organization, OrgAdmin)
class TeamAdmin(admin.ModelAdmin):
raw_id_fields = ('country', 'spr', 'city', 'owner_profile', 'admin_profiles', 'contact_profiles')

View file

@ -0,0 +1,56 @@
from django.core.management.base import BaseCommand, CommandError
from events.models.locale import Country, SPR, City
# Fields from geoname table, from http://download.geonames.org/export/dump/readme.txt
GEONAMEID=0
NAME=1
ASCIINAME=2
ALTERNATENAMES=3
LATITUDE=4
LONGITUDE=5
FEATURE_CLASS=6
FEATURE_CODE=7
COUNTRY_CODE=8
COUNTRY_CODE_2=9
ADMIN1=10
ADMIN2=11
ADMIN3=12
ADMIN4=13
POPULATION=14
ELEVATION=15
DIGITAL_ELEVATION=16
TIMEZONE=17
MODIFICATION_DATE=18
COUNTRY_CACHE = dict()
SPR_CACHE = dict()
class Command(BaseCommand):
help = 'Loads city data from GeoNames database file'
def add_arguments(self, parser):
parser.add_argument('file', type=str)
def handle(self, *args, **options):
if 'file' in options:
# Preload country cache
for country in Country.objects.all():
COUNTRY_CACHE[country.code] = country
for spr in SPR.objects.all():
SPR_CACHE["%s.%s"%(spr.country.code, spr.code)] = spr
cities_file = open(options['file'], 'r')
for city_line in cities_file.readlines():
city = city_line.split("\t")
if len(city) == 19:
if city[FEATURE_CODE] == "PPL" or city[FEATURE_CODE] == "PPLA":
country = COUNTRY_CACHE.get(city[COUNTRY_CODE])
spr = SPR_CACHE.get("%s.%s"%(city[COUNTRY_CODE], city[ADMIN1]))
if country is not None and spr is not None:
City.objects.get_or_create(name=city[NAME], spr=spr, tz=city[TIMEZONE])
else:
print("Short line (%s): %s" % (len(city), city_line))
cities_file.close()
else:
print("No File in options!")

View file

@ -0,0 +1,47 @@
from django.core.management.base import BaseCommand, CommandError
from events.models.locale import Country
# Fields from geoname table, from http://download.geonames.org/export/dump/readme.txt
ISO=0
ISO3=1
ISO_NUMERIC=2
FIPS=3
COUNTRY=4
CAPITAL=5
AREA=6
POPULATION=7
CONTINENT=8
TLD=9
CURRENCYCODE=10
CURRENCYNAME=11
PHONE=12
POSTAL_CODE_FORMAT=13
POSTAL_CODE_REGEX=14
LANGUAGES=15
GEONAMEID=16
NEIGHBOURS=17
EQUIVALENTFIPSCODE=18
class Command(BaseCommand):
help = 'Loads country data from GeoNames database file'
def add_arguments(self, parser):
parser.add_argument('file', type=str)
def handle(self, *args, **options):
if 'file' in options:
countries_file = open(options['file'], 'r')
for country_line in countries_file.readlines():
if country_line.startswith("#"):
continue
country = country_line.split("\t")
if len(country) == 19:
#print("%s - %s" % (country[ISO], country[COUNTRY]))
Country.objects.get_or_create(name=country[COUNTRY], code=country[ISO])
else:
print("Short line (%s): %s" % (len(country), country_line))
countries_file.close()
else:
print("No File in options!")

View file

@ -0,0 +1,41 @@
from django.core.management.base import BaseCommand, CommandError
from events.models.locale import Country, SPR, City
# Fields from geoname table, from http://download.geonames.org/export/dump/readme.txt
COMBINED_CODE=0
NAME=1
ASCIINAME=2
GEONAMEID=3
COUNTRY_CACHE = dict()
class Command(BaseCommand):
help = 'Loads spr data from GeoNames database file'
def add_arguments(self, parser):
parser.add_argument('file', type=str)
def handle(self, *args, **options):
if 'file' in options:
# Preload country cache
for country in Country.objects.all():
COUNTRY_CACHE[country.code] = country
spr_file = open(options['file'], 'r')
for spr_line in spr_file.readlines():
if spr_line.startswith("#"):
continue
spr = spr_line.split("\t")
if len(spr) ==4:
COUNTRY_CODE, SPR_CODE = spr[COMBINED_CODE].split(".")
country = COUNTRY_CACHE.get(COUNTRY_CODE)
if country is not None:
#print("%s - %s, %s" % (SPR_CODE, spr[NAME], country.name))
SPR.objects.get_or_create(name=spr[NAME], code=SPR_CODE, country=country)
else:
print("Short line (%s): %s" % (len(spr), spr_line))
spr_file.close()
else:
print("No File in options!")

File diff suppressed because one or more lines are too long

View file

@ -50,7 +50,11 @@ class Event(models.Model):
#replies
def get_absolute_url(self):
return reverse('show-event', kwargs={'event_id': self.id, 'event_slug': slugify(self.name)})
return reverse('show-event', kwargs={'event_id': self.id, 'event_slug': self.slug})
@property
def slug(self):
return slugify(self.name)
def __str__(self):
return u'%s by %s at %s' % (self.name, self.team.name, self.start_time)

View file

@ -1,6 +1,8 @@
from django.db import models
from django.utils.translation import ugettext_lazy as _
import pytz
class Language(models.Model):
class Meta:
ordering = ('name',)
@ -41,6 +43,7 @@ class Country(models.Model):
class SPR(models.Model):
name = models.CharField(_("Name"), max_length=100)
code = models.CharField(_("Admin Code"), max_length=8)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
class Meta:
@ -63,6 +66,7 @@ class City(models.Model):
name = models.CharField(_("Name"), max_length=100)
spr = models.ForeignKey(SPR, on_delete=models.CASCADE)
tz = models.CharField(max_length=32, verbose_name=_('Default Timezone'), default='UTC', choices=[(tz, tz) for tz in pytz.all_timezones], blank=False, null=False, help_text=_('The most commonly used timezone for this Team.'))
def __str__(self):
return u'%s, %s, %s' % (self.name, self.spr.name, self.spr.country.name)

View file

@ -45,6 +45,19 @@ class UserProfile(models.Model):
local = self.timezone.localize(dt)
return local.astimezone(pytz.utc)
def can_create_event(self, team):
if not self.user_id:
return False
if self.user.is_superuser:
return True
if team.owner_profile == self:
return True
if self in team.admin_profiles.all():
return True
if self in team.contact_profiles.all():
return True
return False
def get_user_timezone(username):
# TODO: find a smarter way to get timezone
return 'UTC'

View file

@ -6,7 +6,7 @@
<td><b>Place:</b></td><td>{{ event.place }}</td>
{% if event.web_url %}
</tr><tr>
<td><b>Website:</b></td><td><a href="{{ event.web_url }}">{{ event.web_url }}</a></td>
<td><b>Website:</b></td><td><a href="{{ event.web_url }}" target="_blank">{{ event.web_url }}</a></td>
{% endif %}
</tr>
</table>

View file

@ -3,8 +3,8 @@
<table border="0" width="960px">
{% for event in events_list %}
<tr>
<td><a href="{{ event.get_absolute_url }}">{{ event.name }}</a></td>
<td>{{ event.team }}</td>
<td><a href="{% url 'show-event' event.id event.slug %}">{{ event.name }}</a></td>
<td><a href="{% url 'show-team' event.team.id %}">{{ event.team.name }}</a></td>
<td>{{ event.start_time }}</td>
<td>{{ event.place }}</td>
</tr>

View file

@ -0,0 +1,18 @@
{% if teams_list %}
<table border="0" width="960px">
{% for team in teams_list %}
<tr>
<td><a href="{% url 'show-team' team.id %}">{{ team.name }}</a></td>
<td>{{ team.country.name|default:'' }}</td>
<td>{{ team.spr.name|default:'' }}</td>
<td>{{ team.city.name|default:'' }}</td>
<td>{{ team.owner_profile }}</td>
<td>{{ team.created_time }}</td>
</tr>
{% endfor %}
</table>
{% else %}
<p>No team available.</p>
{% endif %}

View file

@ -1,8 +1,67 @@
{% if request.user.is_authenticated %}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
Welcome {{ request.user.username }}
<title>Get Together</title>
<!-- Bootstrap core CSS -->
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta.3/css/bootstrap.min.css" integrity="sha384-Zug+QiDoJOrZ5t4lssLdxGhVrurbmBWopoEl+M6BdEfwnCJZtKxi1KgxUyJq13dy" crossorigin="anonymous" rel="stylesheet">
<!-- Bootstrap style overrides -->
<style>
body {
padding-top: 5rem;
}
.starter-template {
padding: 3rem 1.5rem;
text-align: center;
}
</style>
</head>
<body>
<nav class="navbar navbar-expand-md navbar-dark bg-dark fixed-top">
<a class="navbar-brand" href="/">GetTogether</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarsExampleDefault" aria-controls="navbarsExampleDefault" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExampleDefault">
<ul class="navbar-nav mr-auto">
<li class="nav-item active">
<a class="nav-link" href="{% url 'events' %}">Events <span class="sr-only">(current)</span></a>
</li>
<li class="nav-item">
<a class="nav-link" href="{% url 'teams' %}">Teams</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#">Places</a>
</li>
</ul>
<ul class="navbar-nav">
{% if request.user.is_authenticated %}
<li class="nav-item"><a class="nav-link">Welcome {{ request.user.username }}</a></li>
{% else %}
<li class="nav-item"><a class="nav-link" href="/">Signup or Login</a></li>
{% endif %}
</div>
</nav>
<main role="main" class="container">
{% block content %}{% endblock %}
</main>
<!-- Bootstrap core JavaScript
================================================== -->
<!-- Placed at the end of the document so the pages load faster -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
<script>window.jQuery || document.write('<script src="../../../../assets/js/vendor/jquery-slim.min.js"><\/script>')</script>
<script src="../../../../assets/js/vendor/popper.min.js"></script>
<script src="../../../../dist/js/bootstrap.min.js"></script> </body>
</html>

View file

@ -1,12 +1,14 @@
{% extends "get_together/base.html" %}
{% block content %}
<h2>Have a Get Together</h2>
<h2>Plan a Get Together</h2>
<form action="{% url "create-event" team.id%}" method="post">
{% csrf_token %}
<div class="form-group">
{% include "events/event_form.html" %}
<br />
<input type="submit" value="Announce your Get Together!" />
<button type="submit" class="btn btn-primary">Announce your Get Together</button>
</div>
</form>
{% endblock %}

View file

@ -6,7 +6,7 @@
{% csrf_token %}
{% include "events/team_form.html" %}
<br />
<input type="submit" value="Gather your crew!" />
<button type="submit" class="btn btn-primary">Gather your Team</button>
</form>
{% endblock %}

View file

@ -1,6 +1,7 @@
{% extends "get_together/base.html" %}
{% block content %}
<h4>Upcoming events</h4>
{% include "events/event_list.html" %}
{% endblock %}

View file

@ -4,11 +4,11 @@
<center>
<h2>Welcome to Get Together!</h2>
<h3>Login with:</h3>
<a href="{% url 'social:begin' 'google-oauth2' %}">Google</a><br/>
<a href="{% url 'social:begin' 'facebook' %}">Facebook</a><br/>
<a href="{% url 'social:begin' 'twitter' %}">Twitter</a><br/>
<a href="{% url 'social:begin' 'github' %}">GitHub</a><br/>
<h3>Login</h3>
<a class="btn btn-primary" href="{% url 'social:begin' 'google-oauth2' %}">Google</a>&nbsp; &nbsp;
<a class="btn btn-primary" href="{% url 'social:begin' 'facebook' %}">Facebook</a><br/><br/>
<a class="btn btn-primary" href="{% url 'social:begin' 'twitter' %}">Twitter</a>&nbsp; &nbsp;
<a class="btn btn-primary" href="{% url 'social:begin' 'github' %}">GitHub</a><br/>
<br>
</center>
{% endblock %}

View file

@ -3,7 +3,7 @@
{% block content %}
<h2>About {{ event.name }}</h2>
<h3>Hosted by <a href="{% url "show-team" team.id %}">{{ team.name }}</a></h3>
<h4>Hosted by <a href="{% url "show-team" team.id %}">{{ team.name }}</a></h4>
{% include "events/event_details.html" %}
{% endblock %}

View file

@ -3,11 +3,13 @@
{% block content %}
<h2>Welcome to {{ team.name }}</h2>
<h3>Upcoming Events</h3>
<h4>Upcoming Events</h4>
{% include "events/event_list.html" %}
{% if can_create_event %}
<br />
<form action="{% url 'create-event' team.id %}" method="get">
<input type="submit" value="Plan a Get Together!" />
<button type="submit" class="btn btn-primary">Plan a Get Together</button>
</form>
{% endif %}
{% endblock %}

View file

@ -0,0 +1,13 @@
{% extends "get_together/base.html" %}
{% block content %}
{% include "events/team_list.html" %}
{% if request.user.is_authenticated %}
<br/>
<form action="{% url 'create-team' %}" method="get">
<button type="submit" class="btn btn-primary">Start a Team</button>
</form>
{% endif %}
{% endblock %}

View file

@ -25,6 +25,7 @@ urlpatterns = [
path('events/', views.events_list, name='events'),
path('create-team/', views.create_team, name='create-team'),
path('teams/', views.teams_list, name='teams'),
path('team/<int:team_id>/', views.show_team, name='show-team'),
path('team/<int:team_id>/create-event/', views.create_event, name='create-event'),
path('events/<int:event_id>/<str:event_slug>/', views.show_event, name='show-event'),

View file

@ -22,7 +22,7 @@ def home(request, *args, **kwards):
return render(request, 'get_together/index.html')
def events_list(request, *args, **kwargs):
events = Event.objects.all()
events = Event.objects.order_by('start_time').all()
context = {
'events_list': events,
}
@ -51,12 +51,21 @@ def create_team(request, *args, **kwargs):
else:
return redirect('home')
def teams_list(request, *args, **kwargs):
teams = Team.objects.all()
context = {
'teams_list': teams,
}
return render(request, 'get_together/teams.html', context)
def show_team(request, team_id, *args, **kwargs):
team = Team.objects.get(id=team_id)
team_events = Event.objects.filter(team=team)
context = {
'team': team,
'events_list': team_events,
'can_create_event': request.user.profile.can_create_event(team),
}
return render(request, 'get_together/show_team.html', context)