Skip to content
This repository has been archived by the owner on May 4, 2019. It is now read-only.

More work on initial backend models #10

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions sse_library/library/migrations/0002_add_patrons_and_transactions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# -*- coding: utf-8 -*-
import datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models


class Migration(SchemaMigration):

def forwards(self, orm):
# Adding model 'Transaction'
db.create_table('library_transaction', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('book', self.gf('django.db.models.fields.related.ForeignKey')(related_name='transactions', to=orm['library.Book'])),
('borrower', self.gf('django.db.models.fields.related.ForeignKey')(related_name='transactions', to=orm['library.Patron'])),
('checkout_date', self.gf('django.db.models.fields.DateField')(default=datetime.datetime(2012, 9, 24, 0, 0))),
('due_date', self.gf('django.db.models.fields.DateField')(default=datetime.datetime(2012, 10, 15, 0, 0))),
('return_date', self.gf('django.db.models.fields.DateField')(default=None, null=True, blank=True)),
('is_transaction_completed', self.gf('django.db.models.fields.BooleanField')(default=False)),
))
db.send_create_signal('library', ['Transaction'])

# Adding model 'Patron'
db.create_table('library_patron', (
('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('name', self.gf('django.db.models.fields.CharField')(max_length=254)),
('email', self.gf('django.db.models.fields.EmailField')(max_length=7)),
('uid', self.gf('django.db.models.fields.CharField')(max_length=9)),
))
db.send_create_signal('library', ['Patron'])

# Adding field 'Book.is_missing'
db.add_column('library_book', 'is_missing',
self.gf('django.db.models.fields.BooleanField')(default=False),
keep_default=False)


# Changing field 'Book.publisher'
db.alter_column('library_book', 'publisher_id', self.gf('django.db.models.fields.related.ForeignKey')(null=True, to=orm['library.Publisher']))

# Changing field 'Book.isbn'
db.alter_column('library_book', 'isbn', self.gf('django.db.models.fields.CharField')(unique=True, max_length=17))
# Adding unique constraint on 'Book', fields ['isbn']
db.create_unique('library_book', ['isbn'])


# Changing field 'Book.condition'
db.alter_column('library_book', 'condition', self.gf('django.db.models.fields.CharField')(max_length=254))

def backwards(self, orm):
# Removing unique constraint on 'Book', fields ['isbn']
db.delete_unique('library_book', ['isbn'])

# Deleting model 'Transaction'
db.delete_table('library_transaction')

# Deleting model 'Patron'
db.delete_table('library_patron')

# Deleting field 'Book.is_missing'
db.delete_column('library_book', 'is_missing')


# User chose to not deal with backwards NULL issues for 'Book.publisher'
raise RuntimeError("Cannot reverse this migration. 'Book.publisher' and its values cannot be restored.")

# Changing field 'Book.isbn'
db.alter_column('library_book', 'isbn', self.gf('django.db.models.fields.CharField')(max_length=16))

# Changing field 'Book.condition'
db.alter_column('library_book', 'condition', self.gf('django.db.models.fields.CharField')(max_length=1))

models = {
'library.author': {
'Meta': {'object_name': 'Author'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '254'})
},
'library.book': {
'Meta': {'object_name': 'Book'},
'authors': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'books'", 'symmetrical': 'False', 'to': "orm['library.Author']"}),
'condition': ('django.db.models.fields.CharField', [], {'default': "'Good'", 'max_length': '254'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_missing': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_required_text': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'isbn': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '17'}),
'publish_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 9, 24, 0, 0)'}),
'publisher': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'books'", 'null': 'True', 'blank': 'True', 'to': "orm['library.Publisher']"}),
'title': ('django.db.models.fields.CharField', [], {'max_length': '254'})
},
'library.patron': {
'Meta': {'object_name': 'Patron'},
'email': ('django.db.models.fields.EmailField', [], {'max_length': '7'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '254'}),
'uid': ('django.db.models.fields.CharField', [], {'max_length': '9'})
},
'library.publisher': {
'Meta': {'object_name': 'Publisher'},
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '254'})
},
'library.transaction': {
'Meta': {'object_name': 'Transaction'},
'book': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transactions'", 'to': "orm['library.Book']"}),
'borrower': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'transactions'", 'to': "orm['library.Patron']"}),
'checkout_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 9, 24, 0, 0)'}),
'due_date': ('django.db.models.fields.DateField', [], {'default': 'datetime.datetime(2012, 10, 15, 0, 0)'}),
'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_transaction_completed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'return_date': ('django.db.models.fields.DateField', [], {'default': 'None', 'null': 'True', 'blank': 'True'})
}
}

complete_apps = ['library']
149 changes: 117 additions & 32 deletions sse_library/library/models.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,18 @@
from django.db import models
from django.utils import timezone
from datetime import timedelta
import datetime, sha

DEFAULT_CHECKOUT_DURATION = datetime.timedelta(weeks=3)

class CheckoutException(Exception):
pass

class CheckinException(Exception):
pass

class ConsistencyError(Exception):
pass

class Author(models.Model):
""" Author of a book.

Expand All @@ -20,47 +32,120 @@ class Publisher(models.Model):
class Book(models.Model):
""" Book model.

Constants:
* DB_GOOD database shortand for a book in good condition
* DB_POOR database shortand for a book in poor condition
* CONDITIONS mapping from database shorthands to human-readable name

Relations:
* has many authors
* has one publisher
* has many transactions
"""

DB_GOOD = '+'
DB_POOR = '-'
CONDITIONS = (
(DB_GOOD, 'Good'),
(DB_POOR, 'Poor'))

# Foreign Keys
authors = models.ManyToManyField('Author', related_name='books')
publisher = models.ForeignKey('Publisher', related_name='books')
publisher = models.ForeignKey('Publisher',
related_name='books',
null=True,
blank=True,
default=None)

# Fields
title = models.CharField(max_length=254)
isbn = models.CharField(max_length=16)
isbn = models.CharField(max_length=17, unique=True)
publish_date = models.DateField(default=timezone.now())
condition = models.CharField(max_length=1, choices=CONDITIONS, default=DB_GOOD)
is_required_text = models.BooleanField()
condition = models.CharField(max_length=254, default='Good')
is_missing = models.BooleanField(default=False)
is_required_text = models.BooleanField(default=False)

class Patron(models.Model):
""" Library user who borrows books.

Relations:
* has many transactions
"""

# Fields
name = models.CharField(max_length=254)
email = models.EmailField(max_length=7)
uid = models.CharField(max_length=9)

def checked_out_books(self):
""" Returns the patron's currently checked out books.

Returns a generator.

Returns:
collection of books checked out
"""

return (t.book for t in self.transactions.filter(
is_transaction_completed=False))

def checkout(self, book):
""" Attempt to check out a book.

Exceptions:
Book is already checked out by someone
Book is missing
"""

if book.is_missing:
raise CheckoutException("{0} is missing.".format(book.title))

if book.is_required_text:
raise CheckoutException("{0} is a required textbook.".format(book.title))

active_book_transactions = Transaction.objects.filter(
book=book, is_transaction_completed=False)

if active_book_transactions.exists():
existing_transaction = active_book_transactions.all()[0]
raise CheckoutException("{0} is currently checked out by {1}".format(
book.title, existing_transaction.borrower.name))

# Create and save transaction
checkout_transaction = Transaction.objects.create(
book=book, borrower=self)

def checkin(self, book):
""" Attempt to check in (return) a book.

Exceptions:
There is no existing transaction for this user and book.
"""

book_transaction = Transaction.objects.filter(
is_transaction_completed=False, book=book, borrower=self)

if not book_transaction.exists():
raise CheckinException(
"{0} is not checked out by {1} or is already returned".format(
book.title, self.name))

# This should never happen
if book_transaction.count() != 1:
raise ConsistencyError("{0} has multiple transactions with {1}".format(
self.name, book.title))

book_transaction = book_transaction[0]

book_transaction.is_transaction_completed = True
book_transaction.return_date = timezone.now()

book_transaction.save()

class Transaction(models.Model):

ACTIONS = (
('Renew', 'Renew'),
('ChkOut', 'Check Out'),
('ChkIn', 'Check in'))

#Foreign Keys
#TODO:Add in FKs to users
book = models.ForeignKey(Book)

#Fields
date= models.DateTimeField(db_column='Date of Transaction', default = timezone.now())
due_date = models.DateTimeField(db_column='Due Back',default= timezone.now() + timedelta(days=7))
#TODO:Make tables for conditions/actions to make the site more configurable and so that things (such as conditions) can be used in multiple objects
action = models.CharField(max_length=90, choices=ACTIONS, default='ChkOut')
trans_notes = models.CharField(max_length=500)
""" Check out a book.

Relations:
* belongs to a book
* belongs to a borrower
"""

# Foreign Keys
book = models.ForeignKey('Book', related_name='transactions')
borrower = models.ForeignKey('Patron', related_name='transactions')

# Fields
checkout_date = models.DateField(default=timezone.now())
due_date = models.DateField(default=timezone.now() +
DEFAULT_CHECKOUT_DURATION)
return_date = models.DateField(default=None, blank=True, null=True)
is_transaction_completed = models.BooleanField(default=False)
Loading