Source code for feriennet.exports.base

import re
import sedate

from itertools import chain
from html import unescape
from onegov.core import mail
from onegov.feriennet import _
from onegov.feriennet.exports.const import ACTIVITY_STATES
from onegov.feriennet.exports.const import BOOKING_STATES
from onegov.feriennet.exports.const import FREQUENCIES
from onegov.feriennet.exports.const import GENDERS
from onegov.feriennet.exports.const import ROLES
from onegov.feriennet.exports.const import SALUTATIONS
from onegov.feriennet.exports.const import STATES
from onegov.feriennet.utils import decode_name
from onegov.org.models import Export


from typing import Any, TYPE_CHECKING
if TYPE_CHECKING:
    from collections.abc import Iterable, Iterator
    from onegov.activity.models import (
        Activity, Attendee, Booking, InvoiceItem,
        Occasion, OccasionNeed, Volunteer)
    from onegov.user import User

[docs] SPACES = re.compile(r'[ ]+')
[docs] STREET = re.compile(r""" # street name (?P<street>[\D\-\.\s]+) # punctuation between [\s,]* # street number (?P<number>[0-9]{1}[0-9]*\s?[\w]?)? """, re.UNICODE | re.VERBOSE)
[docs] class Street:
[docs] __slots__ = ('name', 'number')
def __init__(self, name: str | None, number: str | None) -> None:
[docs] self.name = name and name.strip(' \n,').title()
[docs] self.number = number and number.strip(' \n,').lower().replace(' ', '')
[docs] def score_street_match(match: re.Match[str] | None) -> int: score = 0 if match: if match.group('street'): score += 1 if match.group('street') and 'strasse' in match.group('street'): score += 1 if match.group('number'): score += 1 return score
[docs] def extract_street(address: str | None) -> Street: if not address or not address.strip(): return Street(None, None) lines = chain(address.splitlines(), (address.replace('\n', ' '), )) matches = [STREET.match(l) for l in lines] match = max(matches, key=score_street_match) if match: return Street(match.group('street'), match.group('number')) return Street(name=address.replace('\n', ''), number=None)
[docs] def remove_duplicate_spaces(text: str) -> str: return SPACES.sub(' ', text)
[docs] def html_to_text(html: str | None) -> str: if not html: return '' return remove_duplicate_spaces(mail.html_to_text( unescape(html), ul_item_mark='•', strong_mark='', emphasis_mark='' ))
[docs] class FeriennetExport(Export):
[docs] def activity_fields( self, activity: 'Activity' ) -> 'Iterator[tuple[str, Any]]': yield _('Activity Title'), activity.title yield _('Activity Lead'), activity.lead yield _('Activity Text'), html_to_text(activity.text) yield _('Activity Text (HTML)'), activity.text yield _('Activity Status'), ACTIVITY_STATES[activity.state] yield _('Activity Location'), activity.location yield _('Activity Tags'), '\n'.join(sorted(activity.tags or []))
[docs] def booking_fields( self, booking: 'Booking' ) -> 'Iterator[tuple[str, Any]]': yield _('Booking State'), BOOKING_STATES[booking.state] yield _('Booking Priority'), booking.priority yield _('Booking Cost'), booking.cost local_booking_time = sedate.to_timezone( booking.created, booking.period.timezone) yield _('Booking Date'), local_booking_time.date()
[docs] def attendee_fields( self, attendee: 'Attendee' ) -> 'Iterator[tuple[str, Any]]': first_name, last_name = decode_name(attendee.name) yield _('Attendee First Name'), first_name or '' yield _('Attendee Last Name'), last_name or '' yield _('Attendee Birth Date'), attendee.birth_date yield _('Attendee Gender'), GENDERS.get(attendee.gender, '') yield _('Attendee Notes'), attendee.notes yield _('Attendee Booking-Limit'), attendee.limit or ''
[docs] def occasion_fields( self, occasion: 'Occasion' ) -> 'Iterator[tuple[str, Any]]': dates = [ (d.localized_start, d.localized_end) for d in occasion.dates ] yield _('Occasion Rescinded'), occasion.cancelled yield _('Occasion Dates'), dates yield _('Occasion Note'), occasion.note yield _('Occasion Age'), '{} - {}'.format( occasion.age.lower, occasion.age.upper - 1) yield _('Occasion Spots'), '{} - {}'.format( occasion.spots.lower, occasion.spots.upper - 1) yield _('Occasion Cost'), occasion.cost or 0 yield _('Occasion Custom Booking Cost'), occasion.booking_cost or 0 yield _('Occasion Meeting Point'), occasion.meeting_point yield _('Occasion May Overlap'), occasion.exclude_from_overlap_check
[docs] def occasion_need_fields( self, need: 'OccasionNeed' ) -> 'Iterator[tuple[str, Any]]': yield _('Need Number'), '{} - {}'.format( need.number.lower, need.number.upper - 1) yield _('Need Name'), need.name yield _('Need Description'), need.description
[docs] def activity_tags( self, tags: 'Iterable[str] | None' ) -> 'Iterator[tuple[str, Any]]': yield _('Activity Tags'), '\n'.join(sorted(tags or []))
[docs] def user_fields(self, user: 'User') -> 'Iterator[tuple[str, Any]]': user_data = user.data or {} salutation = user_data.get('salutation') first_name, last_name = decode_name(user.realname) status_email = user_data.get('ticket_statistics', 'never') street = extract_street(user_data.get('address', None)) yield _('User Login'), user.username yield _('User Role'), ROLES[user.role] yield _('User Active'), user.active yield _('User Tags'), user_data.get('tags', ()) yield _('User Salutation'), SALUTATIONS.get(salutation, '') yield _('User First Name'), first_name or '' yield _('User Last Name'), last_name or '' yield _('User Organisation'), user_data.get('organisation', '') yield _('User Address'), user_data.get('address', '') yield _('User Street'), street.name or '' yield _('User Street Number'), street.number or '' yield _('User Zipcode'), user_data.get('zip_code', '') yield _('User Location'), user_data.get('place', '') yield ( _('User Political Municipality'), user_data.get('political_municipality', '') ) yield _('User E-Mail'), user_data.get('email', '') yield _('User Phone'), user_data.get('phone', '') yield _('User Emergency'), user_data.get('emergency', '') yield _('User Website'), user_data.get('website', '') yield _('User Bank Account'), user_data.get('bank_account', '') yield _('User Beneficiary'), user_data.get('bank_beneficiary', '') yield _('User Status E-Mail'), FREQUENCIES.get(status_email, '') yield _('User TOS Accepted'), user_data.get('tos_accepted', False)
[docs] def invoice_item_fields( self, item: 'InvoiceItem' ) -> 'Iterator[tuple[str, Any]]': yield _('Invoice Item Group'), item.group yield _('Invoice Item Text'), item.text yield _('Invoice Item Paid'), item.paid yield _('Invoice Item Payment date'), item.payment_date yield _('Invoice Item Transaction ID'), item.tid or '' yield _('Invoice Item Source'), item.source or '' yield _('Invoice Item Unit'), item.unit yield _('Invoice Item Quantity'), item.quantity yield _('Invoice Item Amount'), item.amount yield _('Invoice Item References'), '\n'.join( r.readable for r in item.invoice.references )
[docs] def invoice_attendee_fields( self, attendee: 'Attendee' ) -> 'Iterator[tuple[str, Any]]': yield _('Attendee Address'), attendee.address if attendee else '' yield _('Attendee Zipcode'), attendee.zip_code if attendee else '' yield _('Attendee Place'), attendee.place if attendee else '' yield _('Attendee Political Municipality' ), attendee.political_municipality if attendee else ''
[docs] def organiser_fields( self, organiser: 'User' ) -> 'Iterator[tuple[str, Any]]': user_data = organiser.data or {} first_name, last_name = decode_name(organiser.realname) street = extract_street(user_data.get('address', None)) yield _('Organiser Last Name'), last_name or '' yield _('Organiser First Name'), first_name or '' yield _('Organiser Organisation'), user_data.get('organisation', '') yield _('Organiser Address'), user_data.get('address', '') yield _('Organiser Street'), street.name or '' yield _('Organiser Street Number'), street.number or '' yield _('Organiser Zipcode'), user_data.get('zip_code', '') yield _('Organiser Location'), user_data.get('place', '') yield _('Organiser E-Mail'), user_data.get('email', '') yield _('Organiser Phone'), user_data.get('phone', '') yield _('Organiser Website'), user_data.get('website', '') yield _('Organiser Bank Account'), user_data.get('bank_account', '') yield _('Organiser Beneficiary'), user_data.get('bank_beneficiary', '')
[docs] def volunteer_fields( self, volunteer: 'Volunteer' ) -> 'Iterator[tuple[str, Any]]': need = volunteer.need occasion = need.occasion activity = occasion.activity dates = [ (d.localized_start, d.localized_end) for d in occasion.dates ] yield _('Activity Title'), activity.title yield _('Occasion Dates'), dates yield _('Occasion Rescinded'), occasion.cancelled yield _('Need Name'), need.name yield _('Need Number'), '{} - {}'.format( need.number.lower, need.number.upper - 1) yield _('Confirmed Volunteers'), sum( v.state == 'confirmed' for v in need.volunteers) yield _('Volunteer State'), STATES[volunteer.state] yield _('First Name'), volunteer.first_name yield _('Last Name'), volunteer.last_name yield _('Birth Date'), volunteer.birth_date yield _('Organisation'), volunteer.organisation yield _('Place'), volunteer.place yield _('E-Mail'), volunteer.email yield _('Phone'), volunteer.phone yield _('Address'), volunteer.address