importers: New importer for North Bay Python invoices.

This probably has no use outside Conservancy.
This commit is contained in:
Brett Smith 2017-12-17 20:46:00 -05:00
parent a924d9fb1f
commit 93fffe6666
5 changed files with 1454 additions and 0 deletions

View file

@ -0,0 +1,151 @@
import decimal
import functools
import bs4
from .. import util
class Invoice2017:
STANDARD_TICKET_RATE = decimal.Decimal('42.50')
DISCOUNT_TICKET_RATE = STANDARD_TICKET_RATE / 2
STANDARD_SHIRT_RATE = decimal.Decimal('25.50')
DISCOUNT_SHIRT_RATE = STANDARD_SHIRT_RATE
DATE_FMT = '%b. %d, %Y,'
CURRENCY = 'USD'
@classmethod
def _elem_stripped_string(cls, elem):
return ''.join(elem.stripped_strings)
@classmethod
def _table_row_text(cls, table_elem):
for row in table_elem.find_all('tr'):
row_text = []
for cell in row.find_all(('th', 'td')):
row_text.append(cls._elem_stripped_string(cell))
try:
extra_cols = int(cell['colspan'])
except KeyError:
pass
else:
row_text.extend(None for _ in range(extra_cols - 1))
yield row_text
def __init__(self, source_file):
soup = bs4.BeautifulSoup(source_file, 'html5lib')
for table in soup.find_all('table'):
rows_text = self._table_row_text(table)
first_row_text = next(rows_text, [])
if first_row_text[:1] == ['Number']:
handler = self._read_invoice_header
elif first_row_text == ['Description', 'Quantity', 'Price/Unit', 'Total']:
handler = self._read_invoice_items
elif first_row_text == ['Payment time', 'Reference', 'Amount']:
handler = self._read_invoice_activity
else:
continue
handler(table, first_row_text, rows_text)
self.base_data = {
'amount': self.amount,
'currency': self.CURRENCY,
'invoice_id': self.invoice_id,
'payee': self.payee,
'shirt_rate': self.shirt_rate,
'shirts_sold': self.shirts_sold,
'ticket_rate': self.ticket_rate,
'tickets_sold': self.tickets_sold,
}
# Raise an AttributeError if we didn't read any invoice activity.
self.actions
def _read_invoice_header(self, table, first_row_text, rows_text):
self.invoice_id = first_row_text[1]
recipient_h = table.find('th', text='Recipient')
recipient_cell = recipient_h.find_next_sibling('td')
self.payee = next(recipient_cell.stripped_strings)
def _read_invoice_items(self, table, first_row_text, rows_text):
self.amount = decimal.Decimal(0)
self.tickets_sold = decimal.Decimal(0)
self.ticket_rate = self.STANDARD_TICKET_RATE
self.shirts_sold = decimal.Decimal(0)
self.shirt_rate = self.STANDARD_SHIRT_RATE
for description, qty, unit_price, total in rows_text:
if description.startswith('Ticket - '):
self.tickets_sold += 1
elif description.startswith('T-Shirt - '):
self.shirts_sold += 1
elif description.startswith('Early Bird ('):
self.ticket_rate = self.DISCOUNT_TICKET_RATE
if qty:
self.amount += decimal.Decimal(total.lstrip('$'))
def _read_invoice_activity(self, table, first_row_text, rows_text):
self.actions = []
date_wordcount = self.DATE_FMT.count(' ') + 1
for timestamp, action, amount in rows_text:
if action.startswith('Paid '):
last_stripe_id = action.rsplit(None, 1)[1]
action = {
'multiplier': 1,
'payment_id': last_stripe_id,
}
elif action.startswith('Generated credit note '):
action = {
'multiplier': -1,
'payment_id': action.rsplit(None, 1)[1],
}
# Trim extraneous text like the time/a.m./p.m.
date_words = timestamp.split(' ', date_wordcount + 1)[:date_wordcount]
action['date'] = util.strpdate(' '.join(date_words), self.DATE_FMT)
action['stripe_id'] = last_stripe_id
self.actions.append(action)
def __iter__(self):
for action in self.actions:
data = self.base_data.copy()
data.update(action)
multiplier = data.pop('multiplier')
for key in ['amount', 'tickets_sold', 'shirts_sold']:
data[key] *= multiplier
yield data
@functools.lru_cache(5)
def _parse_invoice(parser_class, source_file):
try:
return parser_class(source_file)
except AttributeError:
return None
class ImporterBase:
@classmethod
def _parse_invoice(cls, source_file):
return _parse_invoice(cls.INVOICE_CLASS, source_file)
@classmethod
def can_import(cls, source_file):
return cls._parse_invoice(source_file) is not None
def __init__(self, source_file):
self.invoice = self._parse_invoice(source_file)
def __iter__(self):
for entry in self.invoice:
if self._should_yield_entry(entry):
yield entry
class Payment2017Importer(ImporterBase):
TEMPLATE_KEY = 'template nbpy2017 payment'
INVOICE_CLASS = Invoice2017
def _should_yield_entry(self, entry):
return entry['amount'] > 0
class Refund2017Importer(ImporterBase):
TEMPLATE_KEY = 'template nbpy2017 refund'
INVOICE_CLASS = Invoice2017
def _should_yield_entry(self, entry):
return entry['amount'] < 0

View file

@ -81,3 +81,67 @@
currency: USD currency: USD
payment_id: ch_hHee9ef1aeyee1ruo7ochee9 payment_id: ch_hHee9ef1aeyee1ruo7ochee9
description: "Payment for invoice #100" description: "Payment for invoice #100"
- source: nbpy2017a.html
importer: nbpy2017.Payment2017Importer
expect:
- payee: Python Person A
date: [2017, 10, 19]
amount: "80.00"
tickets_sold: "1.0"
ticket_rate: "21.25"
shirts_sold: "1.0"
shirt_rate: "25.50"
currency: USD
invoice_id: "83"
payment_id: ch_ahr0ue8lai1ohqu4Gei4Biem
stripe_id: ch_ahr0ue8lai1ohqu4Gei4Biem
- source: nbpy2017b.html
importer: nbpy2017.Payment2017Importer
expect:
- payee: Python Person B
date: [2017, 12, 3]
amount: "50.00"
tickets_sold: "1.0"
ticket_rate: "42.50"
shirts_sold: "0.0"
shirt_rate: "25.50"
currency: USD
payment_id: ch_eishei9aiY8aiqu4lieYiu9i
stripe_id: ch_eishei9aiY8aiqu4lieYiu9i
invoice_id: "304"
- source: nbpy2017c.html
importer: nbpy2017.Payment2017Importer
expect:
- payee: Python Person C
date: [2017, 10, 5]
amount: "55.00"
tickets_sold: "1.0"
ticket_rate: "21.25"
shirts_sold: "1.0"
shirt_rate: "25.50"
currency: USD
payment_id: ch_daer0ahwoh9oDeiqu2eimoD7
stripe_id: ch_daer0ahwoh9oDeiqu2eimoD7
invoice_id: "11"
- source: nbpy2017a.html
importer: nbpy2017.Refund2017Importer
expect: []
- source: nbpy2017c.html
importer: nbpy2017.Refund2017Importer
expect:
- payee: Python Person C
date: [2017, 10, 8]
amount: "-55.00"
tickets_sold: "-1.0"
ticket_rate: "21.25"
shirts_sold: "-1.0"
shirt_rate: "25.50"
currency: USD
payment_id: "31"
stripe_id: ch_daer0ahwoh9oDeiqu2eimoD7
invoice_id: "11"

421
tests/data/nbpy2017a.html Normal file
View file

@ -0,0 +1,421 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8" />
<title>North Bay Python | </title>
</head>
<body class="" id="" >
<div class="">
<header>
<div class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="fa fa-bars">
<span class="hidden-accessible">Pages Menu</span>
</span>
</button>
<a class="navbar-brand" href="/">North Bay Python</a>
</div>
<div class="collapse navbar-collapse navbar-responsive-collapse">
<ul class="nav navbar-nav">
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
About
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/about/north-bay-python" >North Bay Python</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/about/petaluma" >Petaluma</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/about/team" >Our Team</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/about/colophon" >Colophon</a>
</li>
</ul>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Attend
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/attend" >Buy a Ticket</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/business-case" >How to Pitch Your Manager</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/travel" >How to Get Here</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/hotels" >Where to Stay</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/code-of-conduct" >Code of Conduct</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/finaid" >Financial Aid</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/guides" >Guide Index</a>
</li>
</ul>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Program
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/program/events" >Events</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/schedule" >Schedule</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/schedule/general-sessions/list/" >Accepted Talks</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/program/call-for-proposals" >Call for Proposals (Closed)</a>
</li>
</ul>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Sponsors
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/sponsors" >Our Sponsors</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/sponsors/become-a-sponsor" >Become a Sponsor</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/donate" >Make a Donation</a>
</li>
</ul>
</li>
<li class=" ">
<a href="/news" >
News
</a>
</li>
<li class=" ">
<a href="/wiki" >
Wiki
</a>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
My Account
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/dashboard" >Dashboard</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/account/logout/" >Log Out</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</header>
<div class="homepage-block-bg website-background"></div>
<div id="background-filter">
<section id="content_body">
<div class="container">
<div class="row">
<div class="col-md-9">
<h1></h1>
<p class="lead">
<div>
<div class="panel-footer">
<a class="btn btn-primary" href="/tickets/invoice/83/refund">Refund by issuing credit note</a>
<a class="btn btn-default" href="/tickets/invoice/83/manual_payment">Apply manual payment/refund</a>
<a class="btn btn-default" href="/dashboard/">Return to dashboard</a>
</div>
<div class="panel panel-primary">
<div class="panel-heading">
<h2>
Registration Receipt
</h2>
<div>
North Bay Python. December 2 &amp; 3 2017. Petaluma, California.
</div>
</div>
<table class="table">
<tr><th>Number</th><td> 83</td></tr>
<tr><th>Status</th><td> Paid</td></tr>
<tr><th>Issue date</th><td> Oct. 19, 2017</td></tr>
<tr><th>Due</th><td> Oct. 20, 2017, 10:21 a.m.</td></tr>
<tr><th>Recipient</th><td> Python Person A<br />Address 1<br />Address 2</td></tr>
</table>
<div class="panel-body">
<div class="alert alert-info">
This is a confirmed registration summary for North Bay Python 2017.
</div>
<div class="panel panel-default">
<table class="table table-striped">
<tr>
<th>Description</th>
<th class="text-right">Quantity</th>
<th class="text-right">Price/Unit</th>
<th class="text-right">Total</th>
</tr>
<tr>
<td>Ticket - Individual Supporter</td>
<td class="text-right">1</td>
<td class="text-right">$100.00</td>
<td class="text-right">$100.00</td>
</tr>
<tr>
<td>T-Shirt - Men&#39;s/Straight Cut Size L</td>
<td class="text-right">1</td>
<td class="text-right">$30.00</td>
<td class="text-right">$30.00</td>
</tr>
<tr>
<td>Early Bird (Ticket - Individual Supporter)</td>
<td class="text-right">1</td>
<td class="text-right">$-20.00</td>
<td class="text-right">$-20.00</td>
</tr>
<tr>
<td>Complimentary for ticket holder (Supporter-level and above) (T-Shirt - Men&#39;s/Straight Cut Size L)</td>
<td class="text-right">1</td>
<td class="text-right">$-30.00</td>
<td class="text-right">$-30.00</td>
</tr>
<tr>
<th colspan="3">TOTAL</th>
<td class="text-right">$80.00</td>
</tr>
<tr>
<td colspan="3">Includes donation eligible for tax deduction in the USA:</td>
<td class="text-right">$33.35</td>
</tr>
</table>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Balance</h3>
</div>
<table class="table table-striped">
<tr>
<td colspan="3">Total payments:</td>
<td class="text-right">$80.00</td>
</tr>
<tr>
<td colspan="3">Balance due:</td>
<td class="text-right">$0.00</td>
</tr>
</table>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<h4 class="panel-title">Payments received</h4>
</div>
<table class="table table-striped">
<tr>
<th>Payment time</th>
<th>Reference</th>
<th>Amount</th>
</tr>
<tr>
<td>Oct. 19, 2017, 10:23 a.m.</td>
<td>Paid with Stripe reference: ch_ahr0ue8lai1ohqu4Gei4Biem</td>
<td>80.00</td>
</tr>
</table>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
Contact Information
</h3>
</div>
<div class="panel-body">
<p>
<p>Direct inquiries to <a href="mailto:hello@northbaypython.org">hello@northbaypython.org</a></p>
<p>North Bay Python is run by North Bay and Bay Area locals, as a member project of <a href="https://sfconservancy.org">Software Freedom Conservancy</a>, a 501(c)(3) not-for-profit public charity registered in New York. Software Freedom Conservancy's federal tax-exempt EIN is 41-2203632.</p>
<strong>Mailing Address</strong>
<address>
Software Freedom Conservancy, Inc.<br>
137 MONTAGUE ST STE 380<br>
Brooklyn, NY 11201-3548<br>
</address>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<h3>Sponsors</h3>
<div class="sponsor-list">
<h4>Platinum</h4>
<div>
<a href="http://revsys.com">
<img src="/site_media/media/sponsor_files/new-horizontal-large.png.600x360_q85.png" alt="REVSYS">
</a>
</div>
<div>
<a href="https://www.cloverhealth.com/en/">
<img src="/site_media/media/sponsor_files/Clover.png.600x360_q85.png" alt="Clover Health">
</a>
</div>
<h4>Gold</h4>
<div>
<a href="https://python.org">
<img src="/site_media/media/sponsor_files/PSF_logo.png.600x360_q85.png" alt="Python Software Foundation">
</a>
</div>
<div>
<a href="https://ibm.com">
<img src="/site_media/media/sponsor_files/IBM_logo.png.600x360_q85.png" alt="IBM">
</a>
</div>
<h4>Silver</h4>
<div>
<a href="https://heroku.com">
<img src="/site_media/media/sponsor_files/heroku-logotype-horizontal-purple.png.600x360_q85.png" alt="Heroku">
</a>
</div>
<div>
<a href="https://sfconservancy.org">
<img src="/site_media/media/sponsor_files/conservancy-logo.png.600x360_q85.png" alt="Software Freedom Conservancy">
</a>
</div>
<div>
<a href="http://www.visitpetaluma.com">
<img src="/site_media/media/sponsor_files/visit_test_logo.png.600x360_q85.png" alt="Visit Petaluma">
</a>
</div>
<div>
<a href="https://yelp.com">
<img src="/site_media/media/sponsor_files/yelp-logo-01_2.png.600x360_q85.png" alt="Yelp">
</a>
</div>
<div>
<a href="https://launchdarkly.com/">
<img src="/site_media/media/sponsor_files/g12.png.600x360_q85.png" alt="LaunchDarkly">
</a>
</div>
<div>
<a href="https://djangoproject.org">
<img src="/site_media/media/sponsor_files/django-logo-positive.png.600x360_q85.png" alt="Django Software Foundation">
</a>
</div>
<div>
<a href="https://developers.google.com">
<img src="/site_media/media/sponsor_files/logo_lockup_google_developers_vertical.png.600x360_q85.png" alt="Google Developers">
</a>
</div>
<div>
<a href="https://bloomberg.com">
<img src="/site_media/media/sponsor_files/logoBBGblck_Reg.png.600x360_q85.png" alt="Bloomberg">
</a>
</div>
<div>
<a href="https://paypal.com">
<img src="/site_media/media/sponsor_files/FullColor_Horizontal_Logo_RGB.png.600x360_q85.png" alt="PayPal">
</a>
</div>
<div>
<a href="http://braintreepayments.com/">
<img src="/site_media/media/sponsor_files/braintree.png.600x360_q85.png" alt="Braintree">
</a>
</div>
<div>
<a href="https://indeed.com">
<img src="/site_media/media/sponsor_files/Indeed_Logo_RGB.png.600x360_q85.png" alt="Indeed">
</a>
</div>
<h4>Community Partner</h4>
<div>
<a href="https://www.beawimp.org/">
<img src="/site_media/media/sponsor_files/wimp-logo.png.600x360_q85.png" alt="Web &amp; Interactive Media Professionals">
</a>
</div>
<div>
<a href="https://www.meetup.com/sfhtml5/">
<img src="/site_media/media/sponsor_files/sfhtml-logo-vector-b-120612.png.600x360_q85.png" alt="SFHTML5">
</a>
</div>
<div>
<a href="http://baybridgepython.org/">
<img src="/site_media/media/sponsor_files/bbp_logo.png.600x360_q85.png" alt="Bay Bridge Python">
</a>
</div>
<div>
<a href="https://www.pycascades.com/">
<img src="/site_media/media/sponsor_files/pycascades-logo-dark-on-transparent-800.png.600x360_q85.png" alt="PyCascades">
</a>
</div>
<div>
<a href="https://www.meetup.com/PyLadies-Vancouver/">
<img src="/site_media/media/sponsor_files/pyladies_vancouver.png.600x360_q85.png" alt="PyLadies Vancouver">
</a>
</div>
<div>
<a href="https://www.meetup.com/PyLadies-Silicon-Valley/">
<img src="/site_media/media/sponsor_files/Pyladies1.jpg.600x360_q85.jpg" alt="PyLadies Silicon Valley">
</a>
</div>
<div>
<a href="https://workpetaluma.com">
<img src="/site_media/media/sponsor_files/WORK_logo_sm.png.600x360_q85.png" alt="WORK Petaluma">
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<div class="container">
<footer>
<div class="row">
<div class="logo">
<div class="circle">
<div class="fill" style="background-image: url('/static/images/logo.svg');"></div>
</div>
</div>
<div class="footer-copy">
<p>&copy; 2017 North Bay Python, member project of <a href="https://sfconservancy.org" >Software Freedom Conservancy</a>, a 501(c)(3) charity.</p>
<p>We acknowledge the support of our Platinum sponsors,
REVSYS
and
Clover Health.
<p>
<a href="https://facebook.com/northbaypython">Facebook</a>
| <a href="https://twitter.com/northbaypython">Twitter</a>
| <a href="https://www.youtube.com/channel/UCLc1vUexbRTlRBJcUG9U6ug">YouTube</a>
| <a href="https://webchat.freenode.net/?channels=%23nbpy">IRC</a>
| <a href="/accessibility">Accessibility</a>
| <a href="/code-of-conduct">Code of Conduct</a>
| <a href="/about/colophon">Colophon</a>
| <a href="/terms">Terms and Conditions</a>
</p>
<p>This site is <a href="https://github.com/northbaypython/website">free and open source software</a>, powered by <a href="https://github.com/chrisjrn/symposion/">Symposion</a> and <a href="https://github.com/chrisjrn/registrasion/">Registrasion</a>.</p>
</div>
</div>
</footer>
</div>
</div>
</body>
</html>

403
tests/data/nbpy2017b.html Normal file
View file

@ -0,0 +1,403 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8" />
<title>North Bay Python | </title>
</head>
<body class="" id="" >
<div class="">
<header>
<div class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="fa fa-bars">
<span class="hidden-accessible">Pages Menu</span>
</span>
</button>
<a class="navbar-brand" href="/">North Bay Python</a>
</div>
<div class="collapse navbar-collapse navbar-responsive-collapse">
<ul class="nav navbar-nav">
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
About
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/about/north-bay-python" >North Bay Python</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/about/petaluma" >Petaluma</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/about/team" >Our Team</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/about/colophon" >Colophon</a>
</li>
</ul>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Attend
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/attend" >Buy a Ticket</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/business-case" >How to Pitch Your Manager</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/travel" >How to Get Here</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/hotels" >Where to Stay</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/code-of-conduct" >Code of Conduct</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/finaid" >Financial Aid</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/guides" >Guide Index</a>
</li>
</ul>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Program
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/program/events" >Events</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/schedule" >Schedule</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/schedule/general-sessions/list/" >Accepted Talks</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/program/call-for-proposals" >Call for Proposals (Closed)</a>
</li>
</ul>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Sponsors
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/sponsors" >Our Sponsors</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/sponsors/become-a-sponsor" >Become a Sponsor</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/donate" >Make a Donation</a>
</li>
</ul>
</li>
<li class=" ">
<a href="/news" >
News
</a>
</li>
<li class=" ">
<a href="/wiki" >
Wiki
</a>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
My Account
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/dashboard" >Dashboard</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/account/logout/" >Log Out</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</header>
<div class="homepage-block-bg website-background"></div>
<div id="background-filter">
<section id="content_body">
<div class="container">
<div class="row">
<div class="col-md-9">
<h1></h1>
<p class="lead">
<div>
<div class="panel-footer">
<a class="btn btn-primary" href="/tickets/invoice/304/refund">Refund by issuing credit note</a>
<a class="btn btn-default" href="/tickets/invoice/304/manual_payment">Apply manual payment/refund</a>
<a class="btn btn-default" href="/dashboard/">Return to dashboard</a>
</div>
<div class="panel panel-primary">
<div class="panel-heading">
<h2>
Registration Receipt
</h2>
<div>
North Bay Python. December 2 &amp; 3 2017. Petaluma, California.
</div>
</div>
<table class="table">
<tr><th>Number</th><td> 304</td></tr>
<tr><th>Status</th><td> Paid</td></tr>
<tr><th>Issue date</th><td> Dec. 3, 2017</td></tr>
<tr><th>Due</th><td> Dec. 4, 2017, 10:08 a.m.</td></tr>
<tr><th>Recipient</th><td> Python Person B<br />1234 Main St<br />Anytown, ME 12345-6789<br />United States of America</td></tr>
</table>
<div class="panel-body">
<div class="alert alert-info">
This is a confirmed registration summary for North Bay Python 2017.
</div>
<div class="panel panel-default">
<table class="table table-striped">
<tr>
<th>Description</th>
<th class="text-right">Quantity</th>
<th class="text-right">Price/Unit</th>
<th class="text-right">Total</th>
</tr>
<tr>
<td>Ticket - Unaffiliated Individual</td>
<td class="text-right">1</td>
<td class="text-right">$50.00</td>
<td class="text-right">$50.00</td>
</tr>
<tr>
<th colspan="3">TOTAL</th>
<td class="text-right">$50.00</td>
</tr>
<tr>
<td colspan="3">Includes donation eligible for tax deduction in the USA:</td>
<td class="text-right">$7.50</td>
</tr>
</table>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Balance</h3>
</div>
<table class="table table-striped">
<tr>
<td colspan="3">Total payments:</td>
<td class="text-right">$50.00</td>
</tr>
<tr>
<td colspan="3">Balance due:</td>
<td class="text-right">$0.00</td>
</tr>
</table>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<h4 class="panel-title">Payments received</h4>
</div>
<table class="table table-striped">
<tr>
<th>Payment time</th>
<th>Reference</th>
<th>Amount</th>
</tr>
<tr>
<td>Dec. 3, 2017, 10:09 a.m.</td>
<td>Paid with Stripe reference: ch_eishei9aiY8aiqu4lieYiu9i</td>
<td>50.00</td>
</tr>
</table>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
Contact Information
</h3>
</div>
<div class="panel-body">
<p>
<p>Direct inquiries to <a href="mailto:hello@northbaypython.org">hello@northbaypython.org</a></p>
<p>North Bay Python is run by North Bay and Bay Area locals, as a member project of <a href="https://sfconservancy.org">Software Freedom Conservancy</a>, a 501(c)(3) not-for-profit public charity registered in New York. Software Freedom Conservancy's federal tax-exempt EIN is 41-2203632.</p>
<strong>Mailing Address</strong>
<address>
Software Freedom Conservancy, Inc.<br>
137 MONTAGUE ST STE 380<br>
Brooklyn, NY 11201-3548<br>
</address>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<h3>Sponsors</h3>
<div class="sponsor-list">
<h4>Platinum</h4>
<div>
<a href="http://revsys.com">
<img src="/site_media/media/sponsor_files/new-horizontal-large.png.600x360_q85.png" alt="REVSYS">
</a>
</div>
<div>
<a href="https://www.cloverhealth.com/en/">
<img src="/site_media/media/sponsor_files/Clover.png.600x360_q85.png" alt="Clover Health">
</a>
</div>
<h4>Gold</h4>
<div>
<a href="https://python.org">
<img src="/site_media/media/sponsor_files/PSF_logo.png.600x360_q85.png" alt="Python Software Foundation">
</a>
</div>
<div>
<a href="https://ibm.com">
<img src="/site_media/media/sponsor_files/IBM_logo.png.600x360_q85.png" alt="IBM">
</a>
</div>
<h4>Silver</h4>
<div>
<a href="https://heroku.com">
<img src="/site_media/media/sponsor_files/heroku-logotype-horizontal-purple.png.600x360_q85.png" alt="Heroku">
</a>
</div>
<div>
<a href="https://sfconservancy.org">
<img src="/site_media/media/sponsor_files/conservancy-logo.png.600x360_q85.png" alt="Software Freedom Conservancy">
</a>
</div>
<div>
<a href="http://www.visitpetaluma.com">
<img src="/site_media/media/sponsor_files/visit_test_logo.png.600x360_q85.png" alt="Visit Petaluma">
</a>
</div>
<div>
<a href="https://yelp.com">
<img src="/site_media/media/sponsor_files/yelp-logo-01_2.png.600x360_q85.png" alt="Yelp">
</a>
</div>
<div>
<a href="https://launchdarkly.com/">
<img src="/site_media/media/sponsor_files/g12.png.600x360_q85.png" alt="LaunchDarkly">
</a>
</div>
<div>
<a href="https://djangoproject.org">
<img src="/site_media/media/sponsor_files/django-logo-positive.png.600x360_q85.png" alt="Django Software Foundation">
</a>
</div>
<div>
<a href="https://developers.google.com">
<img src="/site_media/media/sponsor_files/logo_lockup_google_developers_vertical.png.600x360_q85.png" alt="Google Developers">
</a>
</div>
<div>
<a href="https://bloomberg.com">
<img src="/site_media/media/sponsor_files/logoBBGblck_Reg.png.600x360_q85.png" alt="Bloomberg">
</a>
</div>
<div>
<a href="https://paypal.com">
<img src="/site_media/media/sponsor_files/FullColor_Horizontal_Logo_RGB.png.600x360_q85.png" alt="PayPal">
</a>
</div>
<div>
<a href="http://braintreepayments.com/">
<img src="/site_media/media/sponsor_files/braintree.png.600x360_q85.png" alt="Braintree">
</a>
</div>
<div>
<a href="https://indeed.com">
<img src="/site_media/media/sponsor_files/Indeed_Logo_RGB.png.600x360_q85.png" alt="Indeed">
</a>
</div>
<h4>Community Partner</h4>
<div>
<a href="https://www.beawimp.org/">
<img src="/site_media/media/sponsor_files/wimp-logo.png.600x360_q85.png" alt="Web &amp; Interactive Media Professionals">
</a>
</div>
<div>
<a href="https://www.meetup.com/sfhtml5/">
<img src="/site_media/media/sponsor_files/sfhtml-logo-vector-b-120612.png.600x360_q85.png" alt="SFHTML5">
</a>
</div>
<div>
<a href="http://baybridgepython.org/">
<img src="/site_media/media/sponsor_files/bbp_logo.png.600x360_q85.png" alt="Bay Bridge Python">
</a>
</div>
<div>
<a href="https://www.pycascades.com/">
<img src="/site_media/media/sponsor_files/pycascades-logo-dark-on-transparent-800.png.600x360_q85.png" alt="PyCascades">
</a>
</div>
<div>
<a href="https://www.meetup.com/PyLadies-Vancouver/">
<img src="/site_media/media/sponsor_files/pyladies_vancouver.png.600x360_q85.png" alt="PyLadies Vancouver">
</a>
</div>
<div>
<a href="https://www.meetup.com/PyLadies-Silicon-Valley/">
<img src="/site_media/media/sponsor_files/Pyladies1.jpg.600x360_q85.jpg" alt="PyLadies Silicon Valley">
</a>
</div>
<div>
<a href="https://workpetaluma.com">
<img src="/site_media/media/sponsor_files/WORK_logo_sm.png.600x360_q85.png" alt="WORK Petaluma">
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<div class="container">
<footer>
<div class="row">
<div class="logo">
<div class="circle">
<div class="fill" style="background-image: url('/static/images/logo.svg');"></div>
</div>
</div>
<div class="footer-copy">
<p>&copy; 2017 North Bay Python, member project of <a href="https://sfconservancy.org" >Software Freedom Conservancy</a>, a 501(c)(3) charity.</p>
<p>We acknowledge the support of our Platinum sponsors,
REVSYS
and
Clover Health.
<p>
<a href="https://facebook.com/northbaypython">Facebook</a>
| <a href="https://twitter.com/northbaypython">Twitter</a>
| <a href="https://www.youtube.com/channel/UCLc1vUexbRTlRBJcUG9U6ug">YouTube</a>
| <a href="https://webchat.freenode.net/?channels=%23nbpy">IRC</a>
| <a href="/accessibility">Accessibility</a>
| <a href="/code-of-conduct">Code of Conduct</a>
| <a href="/about/colophon">Colophon</a>
| <a href="/terms">Terms and Conditions</a>
</p>
<p>This site is <a href="https://github.com/northbaypython/website">free and open source software</a>, powered by <a href="https://github.com/chrisjrn/symposion/">Symposion</a> and <a href="https://github.com/chrisjrn/registrasion/">Registrasion</a>.</p>
</div>
</div>
</footer>
</div>
</div>
</body>
</html>

415
tests/data/nbpy2017c.html Normal file
View file

@ -0,0 +1,415 @@
<!DOCTYPE html>
<html lang="en-us">
<head>
<meta charset="utf-8" />
<title>North Bay Python | </title>
</head>
<body class="" id="" >
<div class="">
<header>
<div class="navbar navbar-default navbar-fixed-top">
<div class="container">
<div class="navbar-header">
<button class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse">
<span class="fa fa-bars">
<span class="hidden-accessible">Pages Menu</span>
</span>
</button>
<a class="navbar-brand" href="/">North Bay Python</a>
</div>
<div class="collapse navbar-collapse navbar-responsive-collapse">
<ul class="nav navbar-nav">
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
About
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/about/north-bay-python" >North Bay Python</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/about/petaluma" >Petaluma</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/about/team" >Our Team</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/about/colophon" >Colophon</a>
</li>
</ul>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Attend
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/attend" >Buy a Ticket</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/business-case" >How to Pitch Your Manager</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/travel" >How to Get Here</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/hotels" >Where to Stay</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/code-of-conduct" >Code of Conduct</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/attend/finaid" >Financial Aid</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/guides" >Guide Index</a>
</li>
</ul>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Program
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/program/events" >Events</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/schedule" >Schedule</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/schedule/general-sessions/list/" >Accepted Talks</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/program/call-for-proposals" >Call for Proposals (Closed)</a>
</li>
</ul>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Sponsors
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/sponsors" >Our Sponsors</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/sponsors/become-a-sponsor" >Become a Sponsor</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/donate" >Make a Donation</a>
</li>
</ul>
</li>
<li class=" ">
<a href="/news" >
News
</a>
</li>
<li class=" ">
<a href="/wiki" >
Wiki
</a>
</li>
<li class="dropdown ">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
My Account
<b class="caret"></b>
</a>
<ul class="dropdown-menu" role="menu">
<li role="presentation" >
<a role="menuitem" href="/dashboard" >Dashboard</a>
</li>
<li role="presentation" >
<a role="menuitem" href="/account/logout/" >Log Out</a>
</li>
</ul>
</li>
</ul>
</div>
</div>
</div>
</header>
<div class="homepage-block-bg website-background"></div>
<div id="background-filter">
<section id="content_body">
<div class="container">
<div class="row">
<div class="col-md-9">
<h1></h1>
<p class="lead">
<div>
<div class="panel panel-primary">
<div class="panel-heading">
<h2>
Registration Receipt
</h2>
<div>
North Bay Python. December 2 &amp; 3 2017. Petaluma, California.
</div>
</div>
<table class="table">
<tr><th>Number</th><td> 11</td></tr>
<tr><th>Status</th><td> Refunded</td></tr>
<tr><th>Issue date</th><td> Oct. 5, 2017</td></tr>
<tr><th>Due</th><td> Oct. 6, 2017, 9:09 p.m.</td></tr>
<tr><th>Recipient</th><td> Python Person C<br />Anycountry</td></tr>
</table>
<div class="panel-body">
<div class="alert alert-info">
This is a refunded registration summary for North Bay Python 2017. It is provided for informational purposes only.
</div>
<div class="panel panel-default">
<table class="table table-striped">
<tr>
<th>Description</th>
<th class="text-right">Quantity</th>
<th class="text-right">Price/Unit</th>
<th class="text-right">Total</th>
</tr>
<tr>
<td>Ticket - Unaffiliated Individual</td>
<td class="text-right">1</td>
<td class="text-right">$50.00</td>
<td class="text-right">$50.00</td>
</tr>
<tr>
<td>T-Shirt - Men&#39;s/Straight Cut Size M</td>
<td class="text-right">1</td>
<td class="text-right">$30.00</td>
<td class="text-right">$30.00</td>
</tr>
<tr>
<td>Early Bird (Ticket - Unaffiliated Individual)</td>
<td class="text-right">1</td>
<td class="text-right">$-25.00</td>
<td class="text-right">$-25.00</td>
</tr>
<tr>
<th colspan="3">TOTAL</th>
<td class="text-right">$55.00</td>
</tr>
<tr>
<td colspan="3">Includes donation eligible for tax deduction in the USA:</td>
<td class="text-right">$8.25</td>
</tr>
</table>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<h3 class="panel-title">Balance</h3>
</div>
<table class="table table-striped">
<tr>
<td colspan="3">Total payments:</td>
<td class="text-right">$0</td>
</tr>
<tr>
<td colspan="3">Balance due:</td>
<td class="text-right">$55.00</td>
</tr>
</table>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<h4 class="panel-title">Payments received</h4>
</div>
<table class="table table-striped">
<tr>
<th>Payment time</th>
<th>Reference</th>
<th>Amount</th>
</tr>
<tr>
<td>Oct. 5, 2017, 9:14 p.m.</td>
<td>Paid with Stripe reference: ch_daer0ahwoh9oDeiqu2eimoD7</td>
<td>55.00</td>
</tr>
<tr>
<td>Oct. 8, 2017, 3:20 p.m.</td>
<td>Generated credit note 31</td>
<td>-55.00</td>
</tr>
</table>
</div>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title">
Contact Information
</h3>
</div>
<div class="panel-body">
<p>
<p>Direct inquiries to <a href="mailto:hello@northbaypython.org">hello@northbaypython.org</a></p>
<p>North Bay Python is run by North Bay and Bay Area locals, as a member project of <a href="https://sfconservancy.org">Software Freedom Conservancy</a>, a 501(c)(3) not-for-profit public charity registered in New York. Software Freedom Conservancy's federal tax-exempt EIN is 41-2203632.</p>
<strong>Mailing Address</strong>
<address>
Software Freedom Conservancy, Inc.<br>
137 MONTAGUE ST STE 380<br>
Brooklyn, NY 11201-3548<br>
</address>
</p>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<h3>Sponsors</h3>
<div class="sponsor-list">
<h4>Platinum</h4>
<div>
<a href="http://revsys.com">
<img src="/site_media/media/sponsor_files/new-horizontal-large.png.600x360_q85.png" alt="REVSYS">
</a>
</div>
<div>
<a href="https://www.cloverhealth.com/en/">
<img src="/site_media/media/sponsor_files/Clover.png.600x360_q85.png" alt="Clover Health">
</a>
</div>
<h4>Gold</h4>
<div>
<a href="https://python.org">
<img src="/site_media/media/sponsor_files/PSF_logo.png.600x360_q85.png" alt="Python Software Foundation">
</a>
</div>
<div>
<a href="https://ibm.com">
<img src="/site_media/media/sponsor_files/IBM_logo.png.600x360_q85.png" alt="IBM">
</a>
</div>
<h4>Silver</h4>
<div>
<a href="https://heroku.com">
<img src="/site_media/media/sponsor_files/heroku-logotype-horizontal-purple.png.600x360_q85.png" alt="Heroku">
</a>
</div>
<div>
<a href="https://sfconservancy.org">
<img src="/site_media/media/sponsor_files/conservancy-logo.png.600x360_q85.png" alt="Software Freedom Conservancy">
</a>
</div>
<div>
<a href="http://www.visitpetaluma.com">
<img src="/site_media/media/sponsor_files/visit_test_logo.png.600x360_q85.png" alt="Visit Petaluma">
</a>
</div>
<div>
<a href="https://yelp.com">
<img src="/site_media/media/sponsor_files/yelp-logo-01_2.png.600x360_q85.png" alt="Yelp">
</a>
</div>
<div>
<a href="https://launchdarkly.com/">
<img src="/site_media/media/sponsor_files/g12.png.600x360_q85.png" alt="LaunchDarkly">
</a>
</div>
<div>
<a href="https://djangoproject.org">
<img src="/site_media/media/sponsor_files/django-logo-positive.png.600x360_q85.png" alt="Django Software Foundation">
</a>
</div>
<div>
<a href="https://developers.google.com">
<img src="/site_media/media/sponsor_files/logo_lockup_google_developers_vertical.png.600x360_q85.png" alt="Google Developers">
</a>
</div>
<div>
<a href="https://bloomberg.com">
<img src="/site_media/media/sponsor_files/logoBBGblck_Reg.png.600x360_q85.png" alt="Bloomberg">
</a>
</div>
<div>
<a href="https://paypal.com">
<img src="/site_media/media/sponsor_files/FullColor_Horizontal_Logo_RGB.png.600x360_q85.png" alt="PayPal">
</a>
</div>
<div>
<a href="http://braintreepayments.com/">
<img src="/site_media/media/sponsor_files/braintree.png.600x360_q85.png" alt="Braintree">
</a>
</div>
<div>
<a href="https://indeed.com">
<img src="/site_media/media/sponsor_files/Indeed_Logo_RGB.png.600x360_q85.png" alt="Indeed">
</a>
</div>
<h4>Community Partner</h4>
<div>
<a href="https://www.beawimp.org/">
<img src="/site_media/media/sponsor_files/wimp-logo.png.600x360_q85.png" alt="Web &amp; Interactive Media Professionals">
</a>
</div>
<div>
<a href="https://www.meetup.com/sfhtml5/">
<img src="/site_media/media/sponsor_files/sfhtml-logo-vector-b-120612.png.600x360_q85.png" alt="SFHTML5">
</a>
</div>
<div>
<a href="http://baybridgepython.org/">
<img src="/site_media/media/sponsor_files/bbp_logo.png.600x360_q85.png" alt="Bay Bridge Python">
</a>
</div>
<div>
<a href="https://www.pycascades.com/">
<img src="/site_media/media/sponsor_files/pycascades-logo-dark-on-transparent-800.png.600x360_q85.png" alt="PyCascades">
</a>
</div>
<div>
<a href="https://www.meetup.com/PyLadies-Vancouver/">
<img src="/site_media/media/sponsor_files/pyladies_vancouver.png.600x360_q85.png" alt="PyLadies Vancouver">
</a>
</div>
<div>
<a href="https://www.meetup.com/PyLadies-Silicon-Valley/">
<img src="/site_media/media/sponsor_files/Pyladies1.jpg.600x360_q85.jpg" alt="PyLadies Silicon Valley">
</a>
</div>
<div>
<a href="https://workpetaluma.com">
<img src="/site_media/media/sponsor_files/WORK_logo_sm.png.600x360_q85.png" alt="WORK Petaluma">
</a>
</div>
</div>
</div>
</div>
</div>
</section>
<div class="container">
<footer>
<div class="row">
<div class="logo">
<div class="circle">
<div class="fill" style="background-image: url('/static/images/logo.svg');"></div>
</div>
</div>
<div class="footer-copy">
<p>&copy; 2017 North Bay Python, member project of <a href="https://sfconservancy.org" >Software Freedom Conservancy</a>, a 501(c)(3) charity.</p>
<p>We acknowledge the support of our Platinum sponsors,
REVSYS
and
Clover Health.
<p>
<a href="https://facebook.com/northbaypython">Facebook</a>
| <a href="https://twitter.com/northbaypython">Twitter</a>
| <a href="https://www.youtube.com/channel/UCLc1vUexbRTlRBJcUG9U6ug">YouTube</a>
| <a href="https://webchat.freenode.net/?channels=%23nbpy">IRC</a>
| <a href="/accessibility">Accessibility</a>
| <a href="/code-of-conduct">Code of Conduct</a>
| <a href="/about/colophon">Colophon</a>
| <a href="/terms">Terms and Conditions</a>
</p>
<p>This site is <a href="https://github.com/northbaypython/website">free and open source software</a>, powered by <a href="https://github.com/chrisjrn/symposion/">Symposion</a> and <a href="https://github.com/chrisjrn/registrasion/">Registrasion</a>.</p>
</div>
</div>
</footer>
</div>
</div>
</body>
</html>