generated from hatchways/flask-starter
-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
#60/speed up timezones
- Loading branch information
Showing
6 changed files
with
142 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
|
||
|
@@ -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""" | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters