Skip to content

Commit

Permalink
Merge pull request #69 from hatchways/#63/spped_up_timezones
Browse files Browse the repository at this point in the history
#60/speed up timezones
  • Loading branch information
brian-e-haley authored Feb 27, 2020
2 parents 2b80128 + 6d35133 commit 0adeb27
Show file tree
Hide file tree
Showing 6 changed files with 142 additions and 47 deletions.
28 changes: 18 additions & 10 deletions server/manage.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import csv
from flask.cli import FlaskGroup
from project import create_app, db
from project.models.timezone import Timezone
from project.models.timezone import Timezone, create_timezone
from project.models.user import add_user
import unittest
import sys
from typing import List
import datetime as dt

cli = FlaskGroup(create_app=create_app)

Expand All @@ -14,27 +16,33 @@ def create_db():
print("Recreating db")
db.drop_all()
db.create_all()
seed_timezones('project/db/timezones.csv')
db.session.commit()


@cli.command('seed_db')
def seed_timezones():
"""Creates the timezone table and populates it."""
with open('project/db/timezones.csv') as csv_file:
def seed_timezones(path: str):
"""
Seeds the timezones table with the timezones from path returns the array of
seeded timezones.
:param path: str path to timezones.csv
:return: array of Timezone objects
"""
timezones: List[Timezone] = []
with open(path) as csv_file:
csv_reader = csv.reader(csv_file)
next(csv_reader)
next(csv_reader) # skip the header row
for row in csv_reader:
timezone = Timezone(*row)
db.session.add(timezone)
db.session.commit()
return
timezones.append(create_timezone(*row))
return timezones


def seed_db():
user1 = add_user(name="Brian", email="[email protected]", public_id="Brianh")
user2 = add_user(name="Gerardo", email="[email protected]", public_id="Gerardop")
user3 = add_user(name="Kenneth", email="[email protected]", public_id="kennethc")
db.commit()


@cli.command()
def test():
"""Runs the tests without code coverage"""
Expand Down
3 changes: 3 additions & 0 deletions server/project/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ def create_app(script_info=None):
from project.api.events_handler import events_blueprint
app.register_blueprint(events_blueprint)

from project.api.timezone_handler import timezones_blueprint
app.register_blueprint(timezones_blueprint)

from project.api.calendar_handler import calendar_blueprint
app.register_blueprint(calendar_blueprint)

Expand Down
45 changes: 45 additions & 0 deletions server/project/api/timezone_handler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
from flask import Blueprint
from flask_restx import Resource, fields
from project import api, db
from project.models.timezone import Timezone, hours_minutes

timezones_blueprint = Blueprint('timezone', __name__)


class OffsetHours(fields.Raw):
"""A field that converts a timedelta to an integer hour."""
def format(self, value):
return hours_minutes(value)[0]


class OffsetMinutes(fields.Raw):
"""A field that converts a timedelta to an integer minute."""
def format(self, value):
return hours_minutes(value)[1]


timezones_model = api.model(
'Timezone', {
'name': fields.String(description='The name of the timezone',
example='America/Toronto'),
'offset hours': OffsetHours(description='The UTC offset hours only.',
attribute='offset', required=True,
example=-5),
'offset minutes': OffsetMinutes(description='The UTC offset minutes '
'only.',
attribute='offset', required=True,
example=0),
'dst offset hours': OffsetHours(description='The daylight savings time '
'UTC offset hours only.',
attribute='dst_offset', example=-4),
'dst offset minutes': OffsetMinutes(description='The UTC offset minutes'
' only.',
attribute='dst_offset', example=0)})


@api.route('/timezones')
class Timezones(Resource):
@api.marshal_with(timezones_model)
def get(self):
"""Returns a list of all of the timezones."""
return Timezone.query.all(), 200
41 changes: 31 additions & 10 deletions server/project/models/timezone.py
Original file line number Diff line number Diff line change
@@ -1,19 +1,40 @@
from project import db
import datetime as dt
from typing import Tuple


class Timezone(db.Model):
__tablename__ = "timezones"

id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True, nullable=False)
hours = db.Column(db.Integer, nullable=False)
minutes = db.Column(db.Integer)
dst_hours = db.Column(db.Integer, nullable=False)
dst_minutes = db.Column(db.Integer)
offset = db.Column(db.Interval, nullable=False)
dst_offset = db.Column(db.Interval)

def __init__(self, name, hours, minutes, dst_hours, dst_minutes):
self.name = name
self.hours = hours
self.minutes = minutes
self.dst_hours = dst_hours
self.dst_minutes = dst_minutes

def create_timezone(name: str, hours: str, minutes: str, dst_hours: str,
dst_minutes: str) -> Timezone:
"""
Creates and adds a timezone object returns the created timezone
:param name: the name of the timezone
:param hours: hours portion of the offset
:param minutes: minutes portion of the offset
:param dst_hours: daylight savings hours
:param dst_minutes: daylight savings minutes
:return: Timezone object
"""
timezone = Timezone(
name=name,
offset=dt.timedelta(hours=int(hours), minutes=int(minutes)),
dst_offset=dt.timedelta(hours=int(dst_hours), minutes=int(dst_minutes)))
db.session.add(timezone)
return timezone


def hours_minutes(td: dt.timedelta) -> Tuple[int, int]:
"""
Converts a timedelta to hours and minutes
:param td: timedelta
:return: tuple of integers
"""
return td.days * 24 + td.seconds // 3600, (td.seconds//60) % 60
53 changes: 43 additions & 10 deletions server/project/test/timezone_longtest.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,48 @@
import csv
from project.models.timezone import Timezone
from project.models.timezone import Timezone, hours_minutes
from project.test.timezone_test_base import TimezoneTestBase
import json


class TimezoneModelTest(TimezoneTestBase):
def test_timezone_table(self):
def test_timezone_model(self):
"""Tests whether the timezone table was created successfully."""
with open('project/db/timezones.csv') as csv_file:
csv_reader = csv.reader(csv_file)
next(csv_reader)
counter = 0
for row in csv_reader:
counter += 1
self.assertEqual(Timezone.query.count(), counter)
eastern = 'America/Toronto'
pacific = 'America/Los_Angeles'
est = Timezone.query.filter_by(name=eastern).first()
pst = Timezone.query.filter_by(name=pacific).first()

self.assertEqual(hours_minutes(est.offset)[0], -5)
self.assertEqual(hours_minutes(est.dst_offset)[0], -4)
self.assertEqual(hours_minutes(pst.offset)[0], -8)
self.assertEqual(hours_minutes(pst.dst_offset)[0], -7)


class TimezoneGetTest(TimezoneTestBase):
def test_all_timezones(self):
"""Tests whether the format of the returned timezone is correct."""
response = self.api.get('/timezones', content_type='application/json')
data = json.loads(response.data.decode())

self.assertEqual(len(data), Timezone.query.count())

def test_timezone_marshal(self):
"""Tests whether the format of the returned timezone is correct."""
response = self.api.get('/timezones', content_type='application/json')
data = json.loads(response.data.decode())
eastern_response = None # get rid of warning

for row in data:
if row['name'] == 'America/Toronto':
eastern_response = row
break

eastern_query = Timezone.query.filter_by(name='America/Toronto').first()

self.assertEqual(eastern_response['offset hours'],
hours_minutes(eastern_query.offset)[0])
self.assertEqual(eastern_response['offset minutes'],
hours_minutes(eastern_query.offset)[1])
self.assertEqual(eastern_response['dst offset hours'],
hours_minutes(eastern_query.dst_offset)[0])
self.assertEqual(eastern_response['dst offset minutes'],
hours_minutes(eastern_query.dst_offset)[1])
19 changes: 2 additions & 17 deletions server/project/test/timezone_test_base.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,10 @@
import csv
from project.models.timezone import Timezone
import unittest
from project import create_app, db

# Import this test base if you need to use Timezones db
from manage import seed_timezones

app = create_app()


def seed_timezones():
"""Creates the timezone table and populates it."""
with open('project/db/timezones.csv') as csv_file:
csv_reader = csv.reader(csv_file)
next(csv_reader)
for row in csv_reader:
timezone = Timezone(*row)
db.session.add(timezone)
db.session.commit()
return


class TimezoneTestBase(unittest.TestCase):
"""Test base to be used whenever timezones are needed. This test base will
populate the timezones table and will significantly add to the time
Expand All @@ -31,8 +16,8 @@ def create_app(self):
def setUp(self):
self.api = app.test_client()
db.create_all()
seed_timezones('project/db/timezones.csv')
db.session.commit()
seed_timezones()

def tearDown(self):
db.session.remove()
Expand Down

0 comments on commit 0adeb27

Please sign in to comment.