diff --git a/src/calibre/ebooks/metadata/opf.xml b/src/calibre/ebooks/metadata/opf.xml
index c79ac0f09f..4e0f8d94be 100644
--- a/src/calibre/ebooks/metadata/opf.xml
+++ b/src/calibre/ebooks/metadata/opf.xml
@@ -15,6 +15,7 @@
${mi.comments}
${mi.publisher}
${mi.isbn}
+ ${mi.rights}
diff --git a/src/calibre/ebooks/metadata/opf2.py b/src/calibre/ebooks/metadata/opf2.py
index ed074f813e..b98e345366 100644
--- a/src/calibre/ebooks/metadata/opf2.py
+++ b/src/calibre/ebooks/metadata/opf2.py
@@ -439,7 +439,7 @@ class OPF(object):
publisher = MetadataField('publisher')
language = MetadataField('language')
comments = MetadataField('description')
- category = MetadataField('category')
+ category = MetadataField('type')
rights = MetadataField('rights')
series = MetadataField('series', is_dc=False)
series_index = MetadataField('series_index', is_dc=False, formatter=float, none_is=1)
@@ -967,6 +967,130 @@ class OPFCreator(MetaInformation):
ncx_stream.flush()
+def metadata_to_opf(mi, as_string=True):
+ from lxml import etree
+ import textwrap
+ from calibre.ebooks.oeb.base import OPF, DC
+
+ if not mi.application_id:
+ mi.application_id = str(uuid.uuid4())
+
+ if not mi.book_producer:
+ mi.book_producer = __appname__ + ' (%s) '%__version__ + \
+ '[http://calibre-ebook.com]'
+
+ if not mi.language:
+ mi.language = 'UND'
+
+ root = etree.fromstring(textwrap.dedent(
+ '''
+
+
+ %(id)s
+
+
+
+ '''%dict(a=__appname__, id=mi.application_id)))
+ metadata = root[0]
+ guide = root[1]
+ metadata[0].tail = '\n'+(' '*8)
+ def factory(tag, text=None, sort=None, role=None, scheme=None, name=None,
+ content=None):
+ attrib = {}
+ if sort:
+ attrib[OPF('file-as')] = sort
+ if role:
+ attrib[OPF('role')] = role
+ if scheme:
+ attrib[OPF('scheme')] = scheme
+ if name:
+ attrib['name'] = name
+ if content:
+ attrib['content'] = content
+ elem = metadata.makeelement(tag, attrib=attrib)
+ elem.tail = '\n'+(' '*8)
+ if text:
+ elem.text = text.strip()
+ metadata.append(elem)
+
+ factory(DC('title'), mi.title, mi.title_sort)
+ for au in mi.authors:
+ factory(DC('creator'), au, mi.author_sort, 'aut')
+ factory(DC('contributor'), mi.book_producer, __appname__, 'bkp')
+ if hasattr(mi.pubdate, 'isoformat'):
+ factory(DC('date'), mi.pubdate.isoformat())
+ factory(DC('language'), mi.language)
+ if mi.category:
+ factory(DC('type'), mi.category)
+ if mi.comments:
+ factory(DC('description'), mi.comments)
+ if mi.publisher:
+ factory(DC('publisher'), mi.publisher)
+ if mi.isbn:
+ factory(DC('identifier'), mi.isbn, scheme='ISBN')
+ if mi.rights:
+ factory(DC('rights'), mi.rights)
+ if mi.tags:
+ for tag in mi.tags:
+ factory(DC('subject'), tag)
+ meta = lambda n, c: factory('meta', name='calibre:'+n, content=c)
+ if mi.series:
+ meta('series', mi.series)
+ if mi.series_index is not None:
+ meta('series_index', mi.format_series_index())
+ if mi.rating is not None:
+ meta('rating', str(mi.rating))
+ if hasattr(mi.timestamp, 'isoformat'):
+ meta('timestamp', mi.timestamp.isoformat())
+ if mi.publication_type:
+ meta('publication_type', mi.publication_type)
+
+ metadata[-1].tail = '\n' +(' '*4)
+
+ if mi.cover:
+ guide.text = '\n'+(' '*8)
+ r = guide.makeelement(OPF('reference'),
+ attrib={'type':'cover', 'title':_('Cover'), 'href':mi.cover})
+ r.tail = '\n' +(' '*4)
+ guide.append(r)
+ return etree.tostring(root, pretty_print=True, encoding='utf-8',
+ xml_declaration=True) if as_string else root
+
+
+def test_m2o():
+ from datetime import datetime
+ from cStringIO import StringIO
+ mi = MetaInformation('test & title', ['a"1', "a'2"])
+ mi.title_sort = 'a\'"b'
+ mi.author_sort = 'author sort'
+ mi.pubdate = datetime.now()
+ mi.language = 'en'
+ mi.category = 'test'
+ mi.comments = 'what a fun book\n\n'
+ mi.publisher = 'publisher'
+ mi.isbn = 'boooo'
+ mi.tags = ['a', 'b']
+ mi.series = 's"c\'l&<>'
+ mi.series_index = 3.34
+ mi.rating = 3
+ mi.timestamp = datetime.now()
+ mi.publication_type = 'ooooo'
+ mi.rights = 'yes'
+ mi.cover = 'asd.jpg'
+ opf = metadata_to_opf(mi)
+ print opf
+ newmi = MetaInformation(OPF(StringIO(opf)))
+ for attr in ('author_sort', 'title_sort', 'comments', 'category',
+ 'publisher', 'series', 'series_index', 'rating',
+ 'isbn', 'tags', 'cover_data', 'application_id',
+ 'language', 'cover',
+ 'book_producer', 'timestamp', 'lccn', 'lcc', 'ddc',
+ 'pubdate', 'rights', 'publication_type'):
+ o, n = getattr(mi, attr), getattr(newmi, attr)
+ if o != n and o.strip() != n.strip():
+ print 'FAILED:', attr, getattr(mi, attr), '!=', getattr(newmi, attr)
+
+
class OPFTest(unittest.TestCase):
def setUp(self):
diff --git a/src/calibre/ebooks/metadata/worker.py b/src/calibre/ebooks/metadata/worker.py
index e0e97ad61f..071ec77c23 100644
--- a/src/calibre/ebooks/metadata/worker.py
+++ b/src/calibre/ebooks/metadata/worker.py
@@ -22,7 +22,7 @@ def debug(*args):
def read_metadata_(task, tdir, notification=lambda x,y:x):
from calibre.ebooks.metadata.meta import metadata_from_formats
- from calibre.ebooks.metadata.opf2 import OPFCreator
+ from calibre.ebooks.metadata.opf2 import metadata_to_opf
for x in task:
try:
id, formats = x
@@ -33,9 +33,8 @@ def read_metadata_(task, tdir, notification=lambda x,y:x):
if mi.cover_data:
cdata = mi.cover_data[-1]
mi.cover_data = None
- opf = OPFCreator(tdir, mi)
with open(os.path.join(tdir, '%s.opf'%id), 'wb') as f:
- opf.render(f)
+ f.write(metadata_to_opf(mi))
if cdata:
with open(os.path.join(tdir, str(id)), 'wb') as f:
f.write(cdata)
diff --git a/src/calibre/library/database2.py b/src/calibre/library/database2.py
index 3e218e84cc..d7344b5681 100644
--- a/src/calibre/library/database2.py
+++ b/src/calibre/library/database2.py
@@ -29,7 +29,7 @@ from calibre.ebooks.metadata import string_to_authors, authors_to_string, \
MetaInformation, authors_to_sort_string
from calibre.ebooks.metadata.meta import get_metadata, set_metadata, \
metadata_from_formats
-from calibre.ebooks.metadata.opf2 import OPFCreator
+from calibre.ebooks.metadata.opf2 import metadata_to_opf
from calibre.constants import preferred_encoding, iswindows, isosx, filesystem_encoding
from calibre.ptempfile import PersistentTemporaryFile
from calibre.customize.ui import run_plugins_on_import
@@ -1611,13 +1611,12 @@ books_series_link feeds
id = idx if index_is_id else self.id(idx)
id = str(id)
if not single_dir and not os.path.exists(tpath):
- os.mkdir(tpath)
+ os.makedirs(tpath)
name = au + ' - ' + title if byauthor else title + ' - ' + au
name += '_'+id
base = dir if single_dir else tpath
mi = self.get_metadata(idx, index_is_id=index_is_id, get_cover=True)
- f = open(os.path.join(base, sanitize_file_name(name)+'.opf'), 'wb')
if not mi.authors:
mi.authors = [_('Unknown')]
cdata = self.cover(int(id), index_is_id=True)
@@ -1625,9 +1624,9 @@ books_series_link feeds
cname = sanitize_file_name(name)+'.jpg'
open(os.path.join(base, cname), 'wb').write(cdata)
mi.cover = cname
- opf = OPFCreator(base, mi)
- opf.render(f)
- f.close()
+ with open(os.path.join(base, sanitize_file_name(name)+'.opf'),
+ 'wb') as f:
+ f.write(metadata_to_opf(mi))
fmts = self.formats(idx, index_is_id=index_is_id)
if not fmts:
diff --git a/src/calibre/trac/donations/server.py b/src/calibre/trac/donations/server.py
deleted file mode 100644
index d0fd24af0c..0000000000
--- a/src/calibre/trac/donations/server.py
+++ /dev/null
@@ -1,589 +0,0 @@
-#!/usr/bin/env python
-__license__ = 'GPL v3'
-__copyright__ = '2008, Kovid Goyal kovid@kovidgoyal.net'
-__docformat__ = 'restructuredtext en'
-
-'''
-Keep track of donations to calibre.
-'''
-import sys, cStringIO, textwrap, traceback, re, os, time, calendar
-from datetime import date, timedelta
-from math import sqrt
-os.environ['HOME'] = '/tmp'
-import matplotlib
-matplotlib.use('Agg')
-import matplotlib.pyplot as plt
-import matplotlib.dates as mdates
-
-import cherrypy
-from lxml import etree
-
-def range_for_month(year, month):
- ty, tm = date.today().year, date.today().month
- min = max = date(year=year, month=month, day=1)
- x = date.today().day if ty == year and tm == month else 31
- while x > 1:
- try:
- max = min.replace(day=x)
- break
- except ValueError:
- x -= 1
- return min, max
-
-def range_for_year(year):
- return date(year=year, month=1, day=1), date(year=year, month=12, day=31)
-
-def days_in_month(year, month):
- c = calendar.Calendar()
- ans = 0
- for x in c.itermonthdays(year, month):
- if x != 0: ans += 1
- return ans
-
-def rationalize_country(country):
- if not country:
- return 'Unknown'
- if re.match('(?i)(US|USA|America)', country):
- country = 'USA'
- elif re.match('(?i)(UK|Britain|england)', country):
- country = 'UK'
- elif re.match('(?i)italy', country):
- country = 'Italy'
- elif re.match('(?i)germany', country):
- country = 'Germany'
- elif re.match('(?i)france', country):
- country = 'France'
- elif re.match('(?i)ireland', country):
- country = 'Ireland'
- elif re.match('(?i)norway', country):
- country = 'Norway'
- elif re.match('(?i)canada', country):
- country = 'Canada'
- elif re.match(r'(?i)new\s*zealand', country):
- country = 'New Zealand'
- elif re.match('(?i)jamaica', country):
- country = 'Jamaica'
- elif re.match('(?i)australia', country):
- country = 'Australia'
- elif re.match('(?i)Netherlands', country):
- country = 'Netherlands'
- elif re.match('(?i)spain', country):
- country = 'Spain'
- elif re.match('(?i)colombia', country):
- country = 'Colombia'
- return country
-
-class Record(object):
-
- def __init__(self, email, country, amount, date, name):
- self.email = email
- self.country = country
- self.amount = amount
- self.date = date
- self.name = name
-
- def __str__(self):
- return ''%\
- (self.email, self.country, self.amount, self.date.isoformat(), 'name="%s"'%self.name if self.name else '')
-
-class Country(list):
-
- def __init__(self, name):
- list.__init__(self)
- self.name = name
- self.total = 0.
- self.percent = 0.
-
- def append(self, r):
- self.total += r.amount
- list.append(self, r)
-
- def __str__(self):
- return self.name + ': %.2f%%'%self.percent
-
- def __cmp__(self, other):
- return cmp(self.total, other.total)
-
-
-class Stats:
-
- def get_deviation(self, amounts):
- l = float(len(amounts))
- if l == 0:
- return 0
- mean = sum(amounts)/l
- return sqrt( sum([i**2 for i in amounts])/l - mean**2 )
-
- def __init__(self, records, start, end):
- self.total = sum([r.amount for r in records])
- self.days = {}
- l, rg = date.max, date.min
- self.totals = []
- for r in records:
- self.totals.append(r.amount)
- l, rg = min(l, r.date), max(rg, r.date)
- if r.date not in self.days.keys():
- self.days[r.date] = []
- self.days[r.date].append(r)
-
- self.min, self.max = start, end
- self.period = (self.max - self.min) + timedelta(days=1)
- daily_totals = []
- day = self.min
- while day <= self.max:
- x = self.days.get(day, [])
- daily_totals.append(sum([y.amount for y in x]))
- day += timedelta(days=1)
- self.daily_average = self.total/self.period.days
- self.daily_deviation = self.get_deviation(daily_totals)
- self.average = self.total/len(records) if len(records) else 0.
- self.average_deviation = self.get_deviation(self.totals)
- self.countries = {}
- self.daily_totals = daily_totals
- for r in records:
- if r.country not in self.countries.keys():
- self.countries[r.country] = Country(r.country)
- self.countries[r.country].append(r)
- for country in self.countries.values():
- country.percent = (100 * country.total/self.total) if self.total else 0.
-
- def get_daily_averages(self):
- month_buckets, month_order = {}, []
- x = self.min
- for t in self.daily_totals:
- month = (x.year, x.month)
- if month not in month_buckets:
- month_buckets[month] = 0.
- month_order.append(month)
- month_buckets[month] += t
- x += timedelta(days=1)
- c = calendar.Calendar()
- month_days = [days_in_month(*x) for x in month_order]
- month_averages = [month_buckets[x]/float(y) for x, y in zip(month_order, month_days)]
- return month_order, month_averages
-
-
-
- def __str__(self):
- buf = cStringIO.StringIO()
- print >>buf, '\tTotal: %.2f'%self.total
- print >>buf, '\tDaily Average: %.2f'%self.daily_average
- print >>buf, '\tAverage contribution: %.2f'%self.average
- print >>buf, '\tCountry breakup:'
- for c in self.countries.values():
- print >>buf, '\t\t', c
- return buf.getvalue()
-
- def to_html(self, num_of_countries=sys.maxint):
- countries = sorted(self.countries.values(), cmp=cmp, reverse=True)[:num_of_countries]
- crows = ['
%s | %.2f %% |
'%(c.name, c.percent) for c in countries]
- ctable = '\nCountry | Contribution |
\n%s
'%('\n'.join(crows))
- if num_of_countries < sys.maxint:
- ctable = 'Top %d countries
'%num_of_countries + ctable
- return textwrap.dedent('''
-
-
Donations in %(period)d days [%(min)s — %(max)s]:
-
- Total | $%(total).2f (%(num)d) |
- Daily average | $%(da).2f ± %(dd).2f |
- Average contribution | $%(ac).2f ± %(ad).2f |
- Donors per day | %(dpd).2f |
-
-
- %(ctable)s
-
- ''')%dict(total=self.total, da=self.daily_average, ac=self.average,
- ctable=ctable, period=self.period.days, num=len(self.totals),
- dd=self.daily_deviation, ad=self.average_deviation,
- dpd=len(self.totals)/float(self.period.days),
- min=self.min.isoformat(), max=self.max.isoformat())
-
-
-def expose(func):
-
- def do(self, *args, **kwargs):
- dict.update(cherrypy.response.headers, {'Server':'Donations_server/1.0'})
- return func(self, *args, **kwargs)
-
- return cherrypy.expose(do)
-
-class Server(object):
-
- TRENDS = '/tmp/donations_trend.png'
- MONTH_TRENDS = '/tmp/donations_month_trend.png'
- AVERAGES = '/tmp/donations_averages.png'
-
- def __init__(self, apache=False, root='/', data_file='/tmp/donations.xml'):
- self.apache = apache
- self.document_root = root
- self.data_file = data_file
- self.read_records()
-
- def calculate_daily_averages(self):
- stats = self.get_slice(self.earliest, self.latest)
- fig = plt.figure(2, (10, 4), 96)#, facecolor, edgecolor, frameon, FigureClass)
- fig.clear()
- ax = fig.add_subplot(111)
- month_order, month_averages = stats.get_daily_averages()
- x = [date(y, m, 1) for y, m in month_order[:-1]]
- ax.plot(x, month_averages[:-1])
- ax.set_xlabel('Month')
- ax.set_ylabel('Daily average ($)')
- ax.xaxis.set_major_locator(mdates.MonthLocator(interval=2))
- ax.xaxis.set_major_formatter(mdates.DateFormatter('%m/%y'))
- fig.savefig(self.AVERAGES)
-
-
- def calculate_month_trend(self, days=31):
- stats = self.get_slice(date.today()-timedelta(days=days-1), date.today())
- fig = plt.figure(2, (10, 4), 96)#, facecolor, edgecolor, frameon, FigureClass)
- fig.clear()
- ax = fig.add_subplot(111)
- x = list(range(days-1, -1, -1))
- y = stats.daily_totals
- ax.plot(x, y)#, align='center', width=20, color='g')
- ax.set_xlabel('Days ago')
- ax.set_ylabel('Income ($)')
- ax.hlines([stats.daily_average], 0, days-1)
- ax.hlines([stats.daily_average+stats.daily_deviation,
- stats.daily_average-stats.daily_deviation], 0, days-1,
- linestyle=':',color='r')
- ax.set_xlim([0, days-1])
- text = u'''\
-Total: $%(total).2f
-Daily average: $%(da).2f \u00b1 %(dd).2f
-Average contribution: $%(ac).2f \u00b1 %(ad).2f
-Donors per day: %(dpd).2f
- '''%dict(total=stats.total, da=stats.daily_average,
- dd=stats.daily_deviation, ac=stats.average,
- ad=stats.average_deviation,
- dpd=len(stats.totals)/float(stats.period.days),
- )
- text = ax.annotate(text, (0.5, 0.65), textcoords='axes fraction')
- fig.savefig(self.MONTH_TRENDS)
-
- def calculate_trend(self):
- def months(start, end):
- pos = range_for_month(start.year, start.month)[0]
- while pos <= end:
- yield (pos.year, pos.month)
- if pos.month == 12:
- pos = pos.replace(year = pos.year+1)
- pos = pos.replace(month = 1)
- else:
- pos = pos.replace(month = pos.month + 1)
- _months = list(months(self.earliest, self.latest))[:-1][-12:]
- _months = [range_for_month(*m) for m in _months]
- _months = [self.get_slice(*m) for m in _months]
- x = [m.min for m in _months]
- y = [m.total for m in _months]
- ml = mdates.MonthLocator() # every month
- fig = plt.figure(1, (8, 4), 96)#, facecolor, edgecolor, frameon, FigureClass)
- fig.clear()
- ax = fig.add_subplot(111)
- average = sum(y)/len(y)
- ax.bar(x, y, align='center', width=20, color='g')
- ax.hlines([average], x[0], x[-1])
- ax.xaxis.set_major_locator(ml)
- ax.xaxis.set_major_formatter(mdates.DateFormatter('%b %y'))
- ax.set_xlim(_months[0].min-timedelta(days=15), _months[-1].min+timedelta(days=15))
- ax.set_xlabel('Month')
- ax.set_ylabel('Income ($)')
- fig.autofmt_xdate()
- fig.savefig(self.TRENDS)
- #plt.show()
-
-
- def read_records(self):
- self.tree = etree.parse(self.data_file)
- self.last_read_time = time.time()
- self.root = self.tree.getroot()
- self.records = []
- min_date, max_date = date.today(), date.fromordinal(1)
- for x in self.root.xpath('//donation'):
- d = list(map(int, x.get('date').split('-')))
- d = date(*d)
- self.records.append(Record(x.get('email'), x.get('country'), float(x.get('amount')), d, x.get('name')))
- min_date = min(min_date, d)
- max_date = max(max_date, d)
- self.earliest, self.latest = min_date, max_date
- self.calculate_trend()
- self.calculate_month_trend()
- self.calculate_daily_averages()
-
- def get_slice(self, start_date, end_date):
- stats = Stats([r for r in self.records if r.date >= start_date and r.date <= end_date],
- start_date, end_date)
- return stats
-
- def month(self, year, month):
- return self.get_slice(*range_for_month(year, month))
-
- def year(self, year):
- return self.get_slice(*range_for_year(year))
-
- def range_to_date(self, raw):
- return date(*map(int, raw.split('-')))
-
- def build_page(self, period_type, data):
- if os.stat(self.data_file).st_mtime >= self.last_read_time:
- self.read_records()
- month = date.today().month
- year = date.today().year
- mm = data[1] if period_type == 'month' else month
- my = data[0] if period_type == 'month' else year
- yy = data if period_type == 'year' else year
- rl = data[0] if period_type == 'range' else ''
- rr = data[1] if period_type == 'range' else ''
-
- def build_month_list(current):
- months = []
- for i in range(1, 13):
- month = date(2000, i, 1).strftime('%b')
- sel = 'selected="selected"' if i == current else ''
- months.append(''%(i, sel, month))
- return months
-
- def build_year_list(current):
- all_years = sorted(range(self.earliest.year, self.latest.year+1, 1))
- if current not in all_years:
- current = all_years[0]
- years = []
- for year in all_years:
- sel = 'selected="selected"' if year == current else ''
- years.append(''%(year, sel, year))
- return years
-
- mmlist = ''%('\n'.join(build_month_list(mm)))
- mylist = ''%('\n'.join(build_year_list(my)))
- yylist = ''%('\n'.join(build_year_list(yy)))
-
- if period_type == 'month':
- range_stats = range_for_month(my, mm)
- elif period_type == 'year':
- range_stats = range_for_year(yy)
- else:
- try:
- range_stats = list(map(self.range_to_date, (rl, rr)))
- err = None
- except:
- range_stats = None
- err = traceback.format_exc()
- if range_stats is None:
- range_stats = 'Invalid input:\n%s
'%err
- else:
- range_stats = self.get_slice(*range_stats).to_html(num_of_countries=10)
-
- today = self.get_slice(date.today(), date.today())
-
- return textwrap.dedent('''\
-
-
-
-
- Calibre donations
-
-
-
-
-
-
-
-  |
- Calibre donations |
-
-
-
-
-
-
- Donations to date
- %(todate)s
- |
-
-
- Donations in period
-
- Donations today: $%(today).2f
- %(range_stats)s
- |
-
-
-
-
-
strend.png)
-
Income trends for the last year
-
smonth_trend.png)
-
Income trends for the last 31 days
-
saverage_trend.png)
-
Income trends since records started
-
-
-
- ''')%dict(
- todate=self.get_slice(self.earliest, self.latest).to_html(),
- mc = 'checked="checked"' if period_type=="month" else '',
- yc = 'checked="checked"' if period_type=="year" else '',
- rc = 'checked="checked"' if period_type=="range" else '',
- month_month=mmlist, month_year=mylist, year_year=yylist,
- rl=rl, rr=rr, range_stats=range_stats, root=self.document_root,
- today=today.total
- )
-
- @expose
- def index(self):
- month = date.today().month
- year = date.today().year
- cherrypy.response.headers['Content-Type'] = 'application/xhtml+xml'
- return self.build_page('month', (year, month))
-
- @expose
- def trend_png(self):
- cherrypy.response.headers['Content-Type'] = 'image/png'
- return open(self.TRENDS, 'rb').read()
-
- @expose
- def month_trend_png(self):
- cherrypy.response.headers['Content-Type'] = 'image/png'
- return open(self.MONTH_TRENDS, 'rb').read()
-
- @expose
- def average_trend_png(self):
- cherrypy.response.headers['Content-Type'] = 'image/png'
- return open(self.AVERAGES, 'rb').read()
-
- @expose
- def show(self, period_type='month', month_month='', month_year='',
- year_year='', range_left='', range_right=''):
- if period_type == 'month':
- mm = int(month_month) if month_month else date.today().month
- my = int(month_year) if month_year else date.today().year
- data = (my, mm)
- elif period_type == 'year':
- data = int(year_year) if year_year else date.today().year
- else:
- data = (range_left, range_right)
- cherrypy.response.headers['Content-Type'] = 'application/xhtml+xml'
- return self.build_page(period_type, data)
-
-def config():
- config = {
- 'global': {
- 'tools.gzip.on' : True,
- 'tools.gzip.mime_types': ['text/html', 'text/plain', 'text/xml', 'text/javascript', 'text/css', 'application/xhtml+xml'],
- }
- }
- return config
-
-def apache_start():
- cherrypy.config.update({
- 'log.screen' : False,
- #'log.error_file' : '/tmp/donations.log',
- 'environment' : 'production',
- 'show_tracebacks' : False,
- })
- cherrypy.tree.mount(Server(apache=True, root='/donations/', data_file='/var/www/calibre.kovidgoyal.net/donations.xml'),
- '/donations', config=config())
-
-
-def main(args=sys.argv):
- server = Server()
- cherrypy.quickstart(server, config=config())
- return 0
-
-if __name__ == '__main__':
- sys.exit(main())