<commit_before>import json
import logging
import os
from collections import OrderedDict
from io import BytesIO

from reportlab.lib.enums import TA_CENTER
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import ListStyle, ParagraphStyle, getSampleStyleSheet
from reportlab.lib.units import inch
from reportlab.pdfgen import canvas
from reportlab.platypus import (
    Image, PageBreak, Paragraph, SimpleDocTemplate, Spacer,
)

from django.conf import settings
from django.utils import timezone
from django.utils.html import conditional_escape

from callisto_core.utils import api

logger = logging.getLogger(__name__)

def report_as_pdf(report, data, recipient):
    return PDFFullReport(
        report=report,
        report_data=data,
    ).generate_pdf_report(
        recipient=recipient,
        report_id=report.id,
    )

class MatchReportContent(object):
    '''
        Class to structure contact information collected
        from match submission form for report
    '''

    # This constructor is called with keyword arguments populated by
    # encrypted data. Existing arguments should not be removed or renamed,
    # and new arguments must have default values.
    def __init__(
        self, identifier, perp_name, email, phone,
        contact_name=None, voicemail=None, notes=None,
    ):
        self.identifier = conditional_escape(identifier)
        self.perp_name = conditional_escape(perp_name)
        self.contact_name = conditional_escape(contact_name)
        self.email = conditional_escape(email)
        self.phone = conditional_escape(phone)
        self.voicemail = conditional_escape(voicemail)
        self.notes = conditional_escape(notes)

    @classmethod
    def from_form(cls, form):
        return cls(
            identifier=form.cleaned_data.get('identifier'),
            perp_name=form.cleaned_data.get('perp_name'),
            contact_name=form.instance.report.contact_name,
            email=form.instance.report.contact_email,
            phone=form.instance.report.contact_phone,
            voicemail=form.instance.report.contact_voicemail,
            notes=form.instance.report.contact_notes,
        )

class NumberedCanvas(canvas.Canvas):

    def __init__(self, *args, **kwargs):
        canvas.Canvas.__init__(self, *args, **kwargs)
        self._saved_page_states = []

    def showPage(self):
        self._saved_page_states.append(dict(self.__dict__))
        self._startPage()

    def save(self):
        '''add page info to each page (page x of y)'''
        num_pages = len(self._saved_page_states)
        for state in self._saved_page_states:
            self.__dict__.update(state)
            self.draw_page_number(num_pages)
            canvas.Canvas.showPage(self)
        canvas.Canvas.save(self)

    def draw_page_number(self, page_count):
        width, height = letter
        margin = 0.66 * 72
        self.setFillColor('gray')
        self.drawRightString(
            width - margin, margin, "Page %d of %d" %
            (self._pageNumber, page_count))

class PDFReport(object):

    unselected = u'\u2610'
    selected = u'\u2717'
    free_text = u'\u2756'
    no_bullet = ' '
    report_title = "Report"

    @property
    def headline_style(self):
        styles = getSampleStyleSheet()
        headline_style = styles["Heading1"]
        headline_style.alignment = TA_CENTER
        headline_style.fontSize = 48
        return headline_style

    @property
    def subtitle_style(self):
        styles = getSampleStyleSheet()
        subtitle_style = styles["Heading2"]
        subtitle_style.fontSize = 24
        subtitle_style.leading = 26
        subtitle_style.alignment = TA_CENTER
        return subtitle_style

    def __init__(self):
        self.styles = self.set_up_styles()
        self.notes_style = self.styles['Notes']
        self.answers_style = self.styles['Answers']
        self.answers_list_style = self.styles['AnswersList']
        self.body_style = self.styles['Normal']
        self.section_title_style = self.styles['Heading4']
        self.report_title_style = self.styles['Heading3']
        self.pdf_elements = []

    def set_up_styles(self, *args, **kwargs):
        '''
        Helper formatting methods assume the following styles have been defined:
            * Normal: format_question, get_metadata_page
            * Notes: get_metadata_page
            * Answers: format_answer, get_metadata_page
            * AnswersList: format_answer_list, get_metadata_page
            * Heading4: get_metadata_page, PDFFullReport.generate_pdf_report, PDFMatchReport.generate_match_report
            * Heading3: get_metadata_page, PDFMatchReport.generate_match_report
        If you override this method but don't define these styles, you will need to also override init and potentially
        the corresponding formatting methods.
        :return: a ReportLab stylesheet
        '''
        def get_font_location(filename):
            return settings.APPS_DIR.path('fonts')(filename)

        styles = getSampleStyleSheet()
        headline_style = styles["Heading1"]
        headline_style.alignment = TA_CENTER
        headline_style.fontSize = 48

        subtitle_style = styles["Heading2"]
        subtitle_style.fontSize = 24
        subtitle_style.leading = 26
        subtitle_style.alignment = TA_CENTER

        report_title_style = styles["Heading3"]
        report_title_style.fontSize = 28
        report_title_style.leading = 36
        report_title_style.spaceBefore = 0
        report_title_style.alignment = TA_CENTER

        section_title_style = styles["Heading4"]
        section_title_style.fontSize = 18
        section_title_style.leading = 22
        section_title_style.spaceBefore = 20

        body_style = styles["Normal"]
        body_style.fontSize = 14
        body_style.leading = 17
        body_style.leftIndent = 25

        styles.add(ParagraphStyle(
            name='Notes',
            parent=styles['Normal'],
            fontSize=10,
            leading=14,
            leftIndent=45,
            alias='notes',
        ))
        styles.add(ListStyle(
            name='AnswersList',
        ))
        styles.add(ParagraphStyle(
            name='Answers',
            parent=styles['Normal'],
            leftIndent=0,
            alias='answers',
        ))

        return styles

    # FORMATTING HELPERS

    def add_question(self, question):
        self.pdf_elements.append(
            Paragraph(question, self.body_style),
        )
        self.pdf_elements.append(Spacer(1, 4))

    def add_answer_list(self, answers):
        for answer in answers:
            self.pdf_elements.append(
                Paragraph(answer, self.notes_style),
            )
            self.pdf_elements.append(Spacer(1, 1))

    def render_question(self, question, answers):
        self.add_question(question)
        self.add_answer_list(answers)

    def render_questions(self, report_data):
        for item in report_data:
            question, answers = item.popitem()
            self.render_question(question, answers)

    def get_header_footer(self, recipient):
        def func(canvas, doc):
            width, height = letter
            margin = 0.66 * 72
            canvas.saveState()
            canvas.setFillColor('gray')
            canvas.drawString(margin, height - margin, "CONFIDENTIAL")
            canvas.drawRightString(
                width - margin, height - margin, str(timezone.now()))
            canvas.drawString(
                margin, margin,
                f"Intended for: Title IX Coordinator {recipient}",
            )
            canvas.restoreState()
        return func

    @staticmethod
    def get_user_identifier(user):
        try:
            return user.email or user.username
        except AttributeError:
            return 'Anonymous User'

class ReportPageMixin(object):

    def report_pages(self, reports: list):
        pages = []
        for report in reports:
            pages.extend(self.report_page(report))
            pages.append(PageBreak())
        return pages

    def report_page(self, report):
        return [
            Paragraph(
                "Report",
                self.report_title_style,
            ),
            Paragraph(
                "Report Metadata",
                self.section_title_style,
            ),
            Paragraph(
                f'''
                    Reported by: {self.get_user_identifier(report.owner)}

                    Submitted on: {report.submitted_to_school}

                    Record Created: {report.added.strftime("%Y-%m-%d %H:%M")}

                ''',
                self.body_style,
            ),
            Paragraph(
                "Contact Preferences",
                self.section_title_style,
            ),
            Paragraph(
                f'''
                    Name: {report.contact_name or "None provided"}

                    Phone: {report.contact_phone}

                    Voicemail preferences: {report.contact_voicemail or "None provided"}

                    Email: {report.contact_email}

                    Notes on preferred contact time of day, gender of admin, etc.:
                ''',
                self.body_style,
            ),
            Paragraph(
                report.contact_notes or "None provided",
                self.notes_style,
            )
        ]

class MatchPageMixin(object):

    def match_pages(self, matches: list):
        pages = []
        for match in matches:
            pages.extend(self.match_page(match))
            pages.append(PageBreak())
        return pages

    def match_page(self, match):
        return [
        ]

class PDFUserReviewReport(
    PDFReport,
    ReportPageMixin,
    MatchPageMixin,
):

    title = 'Submitted and Matched Reports'

    def cover_page(self):
        return [
            Image(
                os.path.join(settings.BASE_DIR, api.NotificationApi.logo_path),
                3 * inch,
                3 * inch,
            ),
            Spacer(1, 18),
            Paragraph("CONFIDENTIAL", self.headline_style),
            Spacer(1, 30),
            Spacer(1, 40),
            Paragraph(self.title, self.subtitle_style),
            Spacer(1, 40),
            PageBreak(),
        ]

    @classmethod
    def generate(cls, pdf_input_data: dict):
        # setup
        self = cls()
        reports = pdf_input_data.get('reports', [])
        matches = pdf_input_data.get('matches', [])
        report_buffer = BytesIO()
        doc = SimpleDocTemplate(
            report_buffer,
            pagesize=letter,
            rightMargin=72, leftMargin=72,
            topMargin=72, bottomMargin=72,
        )

        # content fill
        self.pdf_elements.extend(self.cover_page())
        self.pdf_elements.extend(self.report_pages(reports))
        self.pdf_elements.extend(self.match_pages(matches))

        # teardown
        doc.build(
            self.pdf_elements,
            canvasmaker=NumberedCanvas,
        )
        result = report_buffer.getvalue()
        report_buffer.close()
        return result

class PDFFullReport(
    PDFReport,
    ReportPageMixin,
):

    def __init__(self, report, report_data):
        super().__init__()
        self.report = report
        self.report_data = report_data

    def generate_pdf_report(self, report_id, recipient):
        # setup
        report_buffer = BytesIO()
        doc = SimpleDocTemplate(
            report_buffer,
            pagesize=letter,
            rightMargin=72, leftMargin=72,
            topMargin=72, bottomMargin=72,
        )

        # content fill
        self.pdf_elements.extend(
            api.NotificationApi.get_cover_page(
                report_id=report_id,
                recipient=recipient,
            ),
        )
        self.pdf_elements.extend(self.report_page(self.report))
        self.pdf_elements.append(Paragraph("Report Questions", self.section_title_style))
        self.render_questions(self.report_data)

        # teardown
        doc.build(
            self.pdf_elements,
            onFirstPage=self.get_header_footer(recipient),
            onLaterPages=self.get_header_footer(recipient),
            canvasmaker=NumberedCanvas,
        )
        result = report_buffer.getvalue()
        report_buffer.close()
        return result

class PDFMatchReport(PDFReport):

    report_title = "Match Report"

    def __init__(self, matches, identifier):
        super().__init__()
        self.matches = matches
        self.identifier = identifier

    def generate_match_report(self, report_id, recipient):
        '''
        Generates PDF report about a discovered match.

        Args:
          report_id (str): id used to uniquely identify this report to receiving authority

        Returns:
          bytes: a PDF with the submitted perp information & contact information of the reporters for this match

        '''
        # setup :: matches_with_reports
        sorted_matches = sorted(
            self.matches,
            key=lambda m: m.added.strftime("%Y-%m-%d %H:%M"),
            reverse=True,
        )
        matches_with_reports = [
            (
                match,
                MatchReportContent(**json.loads(match.get_match(self.identifier))),
            )
            for match in sorted_matches
        ]
        # setup :: names
        names = ', '.join(OrderedDict.fromkeys([report.perp_name.strip(
        ) for _, report in matches_with_reports if report.perp_name]))
        if len(names) < 1:
            names = 'None provided'
        # setup :: pdf
        buffer = BytesIO()
        doc = SimpleDocTemplate(
            buffer, pagesize=letter,
            rightMargin=72, leftMargin=72,
            topMargin=72, bottomMargin=72,
        )

        # content fill
        self.pdf_elements.extend(
            api.NotificationApi.get_cover_page(
                report_id=report_id,
                recipient=recipient,
            ),
        )
        self.pdf_elements.append(
            Paragraph(
                api.NotificationApi.report_title,
                self.report_title_style,
            )
        )
        self.pdf_elements.append(
            Paragraph(
                "Perpetrator",
                self.section_title_style,
            )
        )
        self.pdf_elements.append(
            Paragraph(
                f'Name(s): {names}
Matching identifier: {self.identifier}',
                self.body_style,
            ),
        )
        # content fill :: reports
        for idx, (match_report, match_report_content) in enumerate(matches_with_reports):
            self.pdf_elements.append(
                Paragraph(
                    "Report " + str(idx + 1),
                    self.section_title_style,
                )
            )

            report = match_report.report
            user = report.owner

            if report.submitted_to_school:
                sent_report = report.sentfullreport_set.first()
                report_id = sent_report.get_report_id() if sent_report else "Not found"
                is_submitted = f'''
                    Yes

                    Submitted to school on: {report.submitted_to_school}

                    Submitted report ID: {report_id}
                '''
            else:
                is_submitted = "No"

            self.pdf_elements.append(
                Paragraph(
                    f'''
                        Perpetrator name given: {match_report_content.perp_name or "None provided"}

                        Reported by: {self.get_user_identifier(user)}

                        Submitted to matching on: {match_report.added.strftime("%Y-%m-%d %H:%M")}

                        Record created: {report.added.strftime("%Y-%m-%d %H:%M")}

                        Full record submitted? {is_submitted}

                        Name: {match_report_content.contact_name or "None provided"}

                        Phone: {match_report_content.phone}

                        Voicemail preferences: {match_report_content.voicemail or "None provided"}

                        Email: {match_report_content.email}

                        Notes on preferred contact time of day, gender of admin, etc.:

                    ''',
                    self.body_style,
                ),
            )
            self.pdf_elements.append(
                Paragraph(
                    match_report_content.notes or "None provided",
                    self.notes_style,
                ),
            )

        # teardown
        doc.build(
            self.pdf_elements,
            onFirstPage=self.get_header_footer(recipient),
            onLaterPages=self.get_header_footer(recipient),
            canvasmaker=NumberedCanvas,
        )
        result = buffer.getvalue()
        buffer.close()
        return result
<commit_msg>two kinds of match pdfs! one partially empty<commit_after>import json
import logging
import os
from collections import OrderedDict
from io import BytesIO

from reportlab.lib.enums import TA_CENTER
from reportlab.lib.pagesizes import letter
from reportlab.lib.styles import ListStyle, ParagraphStyle, getSampleStyleSheet
from reportlab.lib.units import inch
from reportlab.pdfgen import canvas
from reportlab.platypus import (
    Image, PageBreak, Paragraph, SimpleDocTemplate, Spacer,
)

from django.conf import settings
from django.utils import timezone
from django.utils.html import conditional_escape

from callisto_core.utils import api

logger = logging.getLogger(__name__)

def report_as_pdf(report, data, recipient):
    return PDFFullReport(
        report=report,
        report_data=data,
    ).generate_pdf_report(
        recipient=recipient,
        report_id=report.id,
    )

class MatchReportContent(object):
    '''
        Class to structure contact information collected
        from match submission form for report
    '''

    # This constructor is called with keyword arguments populated by
    # encrypted data. Existing arguments should not be removed or renamed,
    # and new arguments must have default values.
    def __init__(
        self, identifier, perp_name, email, phone,
        contact_name=None, voicemail=None, notes=None,
    ):
        self.identifier = conditional_escape(identifier)
        self.perp_name = conditional_escape(perp_name)
        self.contact_name = conditional_escape(contact_name)
        self.email = conditional_escape(email)
        self.phone = conditional_escape(phone)
        self.voicemail = conditional_escape(voicemail)
        self.notes = conditional_escape(notes)

    @classmethod
    def from_form(cls, form):
        return cls(
            identifier=form.cleaned_data.get('identifier'),
            perp_name=form.cleaned_data.get('perp_name'),
            contact_name=form.instance.report.contact_name,
            email=form.instance.report.contact_email,
            phone=form.instance.report.contact_phone,
            voicemail=form.instance.report.contact_voicemail,
            notes=form.instance.report.contact_notes,
        )

    @classmethod
    def empty(cls):
        return cls(
            identifier='',
            perp_name='',
            contact_name='',
            email='',
            phone='',
            voicemail='',
            notes='',
        )

class NumberedCanvas(canvas.Canvas):

    def __init__(self, *args, **kwargs):
        canvas.Canvas.__init__(self, *args, **kwargs)
        self._saved_page_states = []

    def showPage(self):
        self._saved_page_states.append(dict(self.__dict__))
        self._startPage()

    def save(self):
        '''add page info to each page (page x of y)'''
        num_pages = len(self._saved_page_states)
        for state in self._saved_page_states:
            self.__dict__.update(state)
            self.draw_page_number(num_pages)
            canvas.Canvas.showPage(self)
        canvas.Canvas.save(self)

    def draw_page_number(self, page_count):
        width, height = letter
        margin = 0.66 * 72
        self.setFillColor('gray')
        self.drawRightString(
            width - margin, margin, "Page %d of %d" %
            (self._pageNumber, page_count))

class PDFReport(object):

    unselected = u'\u2610'
    selected = u'\u2717'
    free_text = u'\u2756'
    no_bullet = ' '
    report_title = "Report"

    @property
    def headline_style(self):
        styles = getSampleStyleSheet()
        headline_style = styles["Heading1"]
        headline_style.alignment = TA_CENTER
        headline_style.fontSize = 48
        return headline_style

    @property
    def subtitle_style(self):
        styles = getSampleStyleSheet()
        subtitle_style = styles["Heading2"]
        subtitle_style.fontSize = 24
        subtitle_style.leading = 26
        subtitle_style.alignment = TA_CENTER
        return subtitle_style

    def __init__(self):
        self.styles = self.set_up_styles()
        self.notes_style = self.styles['Notes']
        self.answers_style = self.styles['Answers']
        self.answers_list_style = self.styles['AnswersList']
        self.body_style = self.styles['Normal']
        self.section_title_style = self.styles['Heading4']
        self.report_title_style = self.styles['Heading3']
        self.pdf_elements = []

    def set_up_styles(self, *args, **kwargs):
        '''
        Helper formatting methods assume the following styles have been defined:
            * Normal: format_question, get_metadata_page
            * Notes: get_metadata_page
            * Answers: format_answer, get_metadata_page
            * AnswersList: format_answer_list, get_metadata_page
            * Heading4: get_metadata_page, PDFFullReport.generate_pdf_report, PDFMatchReport.generate_match_report
            * Heading3: get_metadata_page, PDFMatchReport.generate_match_report
        If you override this method but don't define these styles, you will need to also override init and potentially
        the corresponding formatting methods.
        :return: a ReportLab stylesheet
        '''
        def get_font_location(filename):
            return settings.APPS_DIR.path('fonts')(filename)

        styles = getSampleStyleSheet()
        headline_style = styles["Heading1"]
        headline_style.alignment = TA_CENTER
        headline_style.fontSize = 48

        subtitle_style = styles["Heading2"]
        subtitle_style.fontSize = 24
        subtitle_style.leading = 26
        subtitle_style.alignment = TA_CENTER

        report_title_style = styles["Heading3"]
        report_title_style.fontSize = 28
        report_title_style.leading = 36
        report_title_style.spaceBefore = 0
        report_title_style.alignment = TA_CENTER

        section_title_style = styles["Heading4"]
        section_title_style.fontSize = 18
        section_title_style.leading = 22
        section_title_style.spaceBefore = 20

        body_style = styles["Normal"]
        body_style.fontSize = 14
        body_style.leading = 17
        body_style.leftIndent = 25

        styles.add(ParagraphStyle(
            name='Notes',
            parent=styles['Normal'],
            fontSize=10,
            leading=14,
            leftIndent=45,
            alias='notes',
        ))
        styles.add(ListStyle(
            name='AnswersList',
        ))
        styles.add(ParagraphStyle(
            name='Answers',
            parent=styles['Normal'],
            leftIndent=0,
            alias='answers',
        ))

        return styles

    # FORMATTING HELPERS

    def add_question(self, question):
        self.pdf_elements.append(
            Paragraph(question, self.body_style),
        )
        self.pdf_elements.append(Spacer(1, 4))

    def add_answer_list(self, answers):
        for answer in answers:
            self.pdf_elements.append(
                Paragraph(answer, self.notes_style),
            )
            self.pdf_elements.append(Spacer(1, 1))

    def render_question(self, question, answers):
        self.add_question(question)
        self.add_answer_list(answers)

    def render_questions(self, report_data):
        for item in report_data:
            question, answers = item.popitem()
            self.render_question(question, answers)

    def get_header_footer(self, recipient):
        def func(canvas, doc):
            width, height = letter
            margin = 0.66 * 72
            canvas.saveState()
            canvas.setFillColor('gray')
            canvas.drawString(margin, height - margin, "CONFIDENTIAL")
            canvas.drawRightString(
                width - margin, height - margin, str(timezone.now()))
            canvas.drawString(
                margin, margin,
                f"Intended for: Title IX Coordinator {recipient}",
            )
            canvas.restoreState()
        return func

    @staticmethod
    def get_user_identifier(user):
        try:
            return user.email or user.username
        except AttributeError:
            return 'Anonymous User'

class ReportPageMixin(object):

    def report_pages(self, reports: list):
        pages = []
        for report in reports:
            pages.extend(self.report_page(report))
            pages.append(PageBreak())
        return pages

    def report_page(self, report):
        return [
            Paragraph(
                "Report",
                self.report_title_style,
            ),
            Paragraph(
                "Report Metadata",
                self.section_title_style,
            ),
            Paragraph(
                f'''
                    Reported by: {self.get_user_identifier(report.owner)}

                    Submitted on: {report.submitted_to_school}

                    Record Created: {report.added.strftime("%Y-%m-%d %H:%M")}

                ''',
                self.body_style,
            ),
            Paragraph(
                "Contact Preferences",
                self.section_title_style,
            ),
            Paragraph(
                f'''
                    Name: {report.contact_name or "None provided"}

                    Phone: {report.contact_phone}

                    Voicemail preferences: {report.contact_voicemail or "None provided"}

                    Email: {report.contact_email}

                    Notes on preferred contact time of day, gender of admin, etc.:
                ''',
                self.body_style,
            ),
            Paragraph(
                report.contact_notes or "None provided",
                self.notes_style,
            )
        ]

class MatchPageMixin(object):

    def match_pages(self, match_report_and_report_content: list):
        pages = []
        for (match_report, match_content) in match_report_and_report_content:
            pages.extend(self.match_page(match_report, match_content))
            pages.append(PageBreak())
        return pages

    def match_page(self, match_report, match_content):
        return [
            Paragraph(
                f'''
                    Perpetrator name given: {match_content.perp_name or "Not available or none provided"}

                    Reported by: {self.get_user_identifier(match_report.report.owner)}

                    Submitted to matching on: {match_report.added.strftime("%Y-%m-%d %H:%M")}

                    Record created: {match_report.report.added.strftime("%Y-%m-%d %H:%M")}

                    Full record submitted? {self._is_submitted(match_report)}

                    Name: {match_report.report.contact_name or "None provided"}

                    Phone: {match_report.report.contact_phone}

                    Voicemail preferences: {match_report.report.contact_voicemail or "None provided"}

                    Email: {match_report.report.contact_email}

                    Notes on preferred contact time of day, gender of admin, etc.:

                ''',
                self.body_style,
            ),
            Paragraph(
                match_report.report.contact_notes or "None provided",
                self.notes_style,
            ),
        ]

    def _is_submitted(self, match_report):
        if match_report.report.submitted_to_school:
            sent_report = match_report.report.sentfullreport_set.first()
            report_id = sent_report.get_report_id() if sent_report else "Not found"
            return f'''
                Yes

                Submitted to school on: {match_report.report.submitted_to_school}

                Submitted report ID: {report_id}
            '''
        else:
            return "No"

class PDFFullReport(
    PDFReport,
    ReportPageMixin,
):

    def __init__(self, report, report_data):
        super().__init__()
        self.report = report
        self.report_data = report_data

    def generate_pdf_report(self, report_id, recipient):
        # setup
        report_buffer = BytesIO()
        doc = SimpleDocTemplate(
            report_buffer,
            pagesize=letter,
            rightMargin=72, leftMargin=72,
            topMargin=72, bottomMargin=72,
        )

        # content fill
        self.pdf_elements.extend(
            api.NotificationApi.get_cover_page(
                report_id=report_id,
                recipient=recipient,
            ),
        )
        self.pdf_elements.extend(self.report_page(self.report))
        self.pdf_elements.append(Paragraph("Report Questions", self.section_title_style))
        self.render_questions(self.report_data)

        # teardown
        doc.build(
            self.pdf_elements,
            onFirstPage=self.get_header_footer(recipient),
            onLaterPages=self.get_header_footer(recipient),
            canvasmaker=NumberedCanvas,
        )
        result = report_buffer.getvalue()
        report_buffer.close()
        return result

class PDFMatchReport(
    PDFReport,
    MatchPageMixin,
):

    report_title = "Match Report"

    def __init__(self, matches, identifier):
        super().__init__()
        self.matches = matches
        self.identifier = identifier

    def names_and_matching_identifiers(self, matches_with_reports):
        names = ', '.join(
            OrderedDict.fromkeys([
                match_report_content.perp_name.strip()
                for _, match_report_content in matches_with_reports
                if match_report_content.perp_name
            ])
        )
        if len(names) < 1:
            names = 'None provided'
        return Paragraph(
            f'Name(s): {names}
Matching identifier: {self.identifier}',
            self.body_style,
        )

    def generate_match_report(self, report_id, recipient):
        '''
        Generates PDF report about a discovered match.

        Args:
          report_id (str): id used to uniquely identify this report to receiving authority

        Returns:
          bytes: a PDF with the submitted perp information & contact information of the reporters for this match

        '''
        # setup :: matches
        sorted_matches = sorted(
            self.matches,
            key=lambda m: m.added.strftime("%Y-%m-%d %H:%M"),
            reverse=True,
        )
        match_report_and_report_content = [
            (
                match,
                MatchReportContent(**json.loads(match.get_match(self.identifier))),
            )
            for match in sorted_matches
        ]
        # setup :: pdf
        buffer = BytesIO()
        doc = SimpleDocTemplate(
            buffer, pagesize=letter,
            rightMargin=72, leftMargin=72,
            topMargin=72, bottomMargin=72,
        )

        # content fill
        self.pdf_elements.extend(
            api.NotificationApi.get_cover_page(
                report_id=report_id,
                recipient=recipient,
            ),
        )
        self.pdf_elements.append(
            Paragraph(
                api.NotificationApi.report_title,
                self.report_title_style,
            )
        )
        self.pdf_elements.append(Paragraph("Perpetrator(s)", self.section_title_style))
        self.pdf_elements.append(self.names_and_matching_identifiers(match_report_and_report_content))
        self.pdf_elements.append(self.match_report_pages(match_report_and_report_content))

        # teardown
        doc.build(
            self.pdf_elements,
            onFirstPage=self.get_header_footer(recipient),
            onLaterPages=self.get_header_footer(recipient),
            canvasmaker=NumberedCanvas,
        )
        result = buffer.getvalue()
        buffer.close()
        return result

class PDFUserReviewReport(
    PDFReport,
    ReportPageMixin,
    MatchPageMixin,
):

    title = 'Submitted and Matched Reports'

    def cover_page(self):
        return [
            Image(
                os.path.join(settings.BASE_DIR, api.NotificationApi.logo_path),
                3 * inch,
                3 * inch,
            ),
            Spacer(1, 18),
            Paragraph("CONFIDENTIAL", self.headline_style),
            Spacer(1, 30),
            Spacer(1, 40),
            Paragraph(self.title, self.subtitle_style),
            Spacer(1, 40),
            PageBreak(),
        ]

    def match_pages_empty_identifier(self, matches):
        match_report_and_report_content = [
            (
                match,
                MatchReportContent.empty(),
            )
            for match in matches
        ]
        return self.match_pages(match_report_and_report_content)

    @classmethod
    def generate(cls, pdf_input_data: dict):
        # setup
        self = cls()
        reports = pdf_input_data.get('reports', [])
        matches = pdf_input_data.get('matches', [])
        report_buffer = BytesIO()
        doc = SimpleDocTemplate(
            report_buffer,
            pagesize=letter,
            rightMargin=72, leftMargin=72,
            topMargin=72, bottomMargin=72,
        )

        # content fill
        self.pdf_elements.extend(self.cover_page())
        self.pdf_elements.extend(self.report_pages(reports))
        self.pdf_elements.extend(self.match_pages_empty_identifier(matches))

        # teardown
        doc.build(
            self.pdf_elements,
            canvasmaker=NumberedCanvas,
        )
        result = report_buffer.getvalue()
        report_buffer.close()
        return result
<|endoftext|>


<commit_before>

"""
.. module:: udp_session

BitPie.NET udp_session() Automat

EVENTS:
    * :red:`datagram-received`
    * :red:`init`
    * :red:`shutdown`
    * :red:`timer-10sec`
    * :red:`timer-1min`
    * :red:`timer-1sec`
    * :red:`timer-30sec`

"""

import time

from twisted.internet import reactor

from logs import lg

from lib import misc
from lib import automat
from lib import udp

import udp_file_queue

#------------------------------------------------------------------------------

MIN_PROCESS_SESSIONS_DELAY = 0.001
MAX_PROCESS_SESSIONS_DELAY = 1.0

#------------------------------------------------------------------------------

_SessionsDict = {}
_SessionsDictByPeerAddress = {}
_SessionsDictByPeerID = {}
_KnownPeersDict = {}
_KnownUserIDsDict = {}
_PendingOutboxFiles = []
_ProcessSessionsTask = None
_ProcessSessionsDelay = MIN_PROCESS_SESSIONS_DELAY

#------------------------------------------------------------------------------

def sessions():
    """
    """
    global _SessionsDict
    return _SessionsDict

def sessions_by_peer_address():
    global _SessionsDictByPeerAddress
    return _SessionsDictByPeerAddress

def sessions_by_peer_id():
    global _SessionsDictByPeerID
    return _SessionsDictByPeerID

def create(node, peer_address, peer_id=None):
    """
    """
    lg.out(10, 'udp_session.create peer_address=%s' % str(peer_address))
    s = UDPSession(node, peer_address, peer_id)
    sessions()[s.id] = s
    try:
        sessions_by_peer_address()[peer_address].append(s)
    except:
        sessions_by_peer_address()[peer_address] = [s,]
    try:
        sessions_by_peer_id()[peer_id].append(s)
    except:
        sessions_by_peer_id()[peer_id] = [s,]
    return s

def get(peer_address):
    """
    """
    # lg.out(18, 'udp_session.get %s %s' % (str(peer_address),
    #     str(map(lambda s:s.peer_address, sessions().values()))))
    for s in sessions_by_peer_address().get(peer_address, []):
        return s
    # for id, s in sessions().items():
    #     if s.peer_address == peer_address:
    #         return s
    return None

def get_by_peer_id(peer_id):
    """
    """
    for s in sessions_by_peer_id().get(peer_id, []):
        return s
    # for id, s in sessions().items():
    #     if s.peer_id == peer_id:
    #         return s
    return None

def close(peer_address):
    """
    """
    s = get(peer_address)
    if s is None:
        return False
    s.automat('shutdown')
    return True

def add_pending_outbox_file(filename, host, description='', result_defer=None, single=False):
    """
    """
    global _PendingOutboxFiles
    _PendingOutboxFiles.append((filename, host, description, result_defer, single, time.time()))

def process_sessions():
    global _ProcessSessionsTask
    global _ProcessSessionsDelay
    has_activity = False
    for s in sessions().values():
        if not s.file_queue:
            continue
        has_outbox = s.file_queue.process_outbox_queue()
        has_sends = s.file_queue.process_outbox_files()
        if has_sends or has_outbox:
            has_activity = True
    if has_activity:
        _ProcessSessionsTask = reactor.callLater(0, process_sessions)
    else:
        _ProcessSessionsDelay = misc.LoopAttenuation(
            _ProcessSessionsDelay, has_activity,
            MIN_PROCESS_SESSIONS_DELAY, MAX_PROCESS_SESSIONS_DELAY,)
        # attenuation
        _ProcessSessionsTask = reactor.callLater(_ProcessSessionsDelay,
                                                 process_sessions)

def stop_process_sessions(self):
    global _ProcessSessionsTask
    if _ProcessSessionsTask:
        if _ProcessSessionsTask.active():
            _ProcessSessionsTask.cancel()
            _ProcessSessionsTask = None

#------------------------------------------------------------------------------

class UDPSession(automat.Automat):
    """
    This class implements all the functionality of the ``udp_session()`` state machine.
    """

    fast = True

    timers = {
        'timer-1min':  (60,   ['CONNECTED']),
        'timer-1sec':  (1.0,  ['PING','GREETING']),
        'timer-30sec': (30.0, ['GREETING']),
        'timer-10sec': (10.0, ['PING','CONNECTED']),
        }

    def __init__(self, node, peer_address, peer_id=None):
        self.node = node
        self.peer_address = peer_address
        self.peer_id = peer_id
        self.peer_idurl = None
        self.last_datagram_received_time = 0
        self.bytes_sent = 0
        self.bytes_received = 0
        self.file_queue = udp_file_queue.FileQueue(self)
        name = 'udp_session[%s:%d]' % (self.peer_address[0], self.peer_address[1])
        automat.Automat.__init__(self, name, 'AT_STARTUP')

    def send_packet(self, command, payload):
        self.bytes_sent += len(payload)
        return udp.send_command(self.node.listen_port, command,
                                payload, self.peer_address)

    def A(self, event, arg):
        #---CONNECTED---
        if self.state == 'CONNECTED':
            if event == 'datagram-received' :
                self.doReceiveData(arg)
            elif event == 'timer-10sec' :
                self.doAlive(arg)
            elif event == 'shutdown' or ( event == 'timer-1min' and not self.isSessionActive(arg) ) :
                self.state = 'CLOSED'
                self.doNotifyDisconnected(arg)
                self.doDestroyMe(arg)
        #---AT_STARTUP---
        elif self.state == 'AT_STARTUP':
            if event == 'init' :
                self.state = 'PING'
                self.doInit(arg)
                self.doPing(arg)
            elif event == 'shutdown' :
                self.state = 'CLOSED'
                self.doDestroyMe(arg)
        #---PING---
        elif self.state == 'PING':
            if event == 'timer-1sec' :
                self.doPing(arg)
            elif event == 'datagram-received' and self.isPing(arg) :
                self.state = 'GREETING'
                self.doReceiveData(arg)
            elif event == 'datagram-received' and self.isGreeting(arg) :
                self.state = 'GREETING'
                self.doReceiveData(arg)
            elif event == 'datagram-received' and not self.isPingOrGreeting(arg) :
                self.doReceiveData(arg)
            elif event == 'shutdown' or event == 'timer-10sec' :
                self.state = 'CLOSED'
                self.doDestroyMe(arg)
        #---GREETING---
        elif self.state == 'GREETING':
            if event == 'timer-1sec' :
                self.doGreeting(arg)
            elif event == 'shutdown' or event == 'timer-30sec' :
                self.state = 'CLOSED'
                self.doDestroyMe(arg)
            elif event == 'datagram-received' and not self.isGreetingOrAlive(arg) :
                self.doReceiveData(arg)
            elif event == 'datagram-received' and self.isGreetingOrAlive(arg) :
                self.state = 'CONNECTED'
                self.doReceiveData(arg)
                self.doNotifyConnected(arg)
                self.doCheckPendingFiles(arg)
        #---CLOSED---
        elif self.state == 'CLOSED':
            pass

    def isPing(self, arg):
        """
        Condition method.
        """
        command = arg[0][0]
        return command == udp.CMD_PING

    def isGreeting(self, arg):
        """
        Condition method.
        """
        command = arg[0][0]
        return command == udp.CMD_GREETING

    def isPingOrGreeting(self, arg):
        """
        Condition method.
        """
        command = arg[0][0]
        return ( command == udp.CMD_PING or command == udp.CMD_GREETING)

    def isGreetingOrAlive(self, arg):
        """
        Condition method.
        """
        command = arg[0][0]
        return ( command == udp.CMD_ALIVE or command == udp.CMD_GREETING)

    def isSessionActive(self, arg):
        """
        Condition method.
        """
        return time.time() - self.last_datagram_received_time < 60

    def doInit(self, arg):
        """
        Action method.
        """
        # self.listen_port, self.my_id, self.my_address = arg

    def doAlive(self, arg):
        """
        Action method.
        """
        udp.send_command(self.node.listen_port, udp.CMD_ALIVE, '', self.peer_address)

    def doGreeting(self, arg):
        """
        Action method.
        """
        payload = str(self.node.my_id)+' '+str(self.node.my_idurl)
        udp.send_command(self.node.listen_port, udp.CMD_GREETING, payload, self.peer_address)

    def doPing(self, arg):
        """
        Action method.
        """
        udp.send_command(self.node.listen_port, udp.CMD_PING, '', self.peer_address)

    def doReceiveData(self, arg):
        """
        Action method.
        """
        self.last_datagram_received_time = time.time()
        try:
            datagram, address = arg
            command, payload = datagram
        except:
            return
        assert address == self.peer_address
        self.bytes_received += len(payload)
        if command == udp.CMD_DATA:
            self.file_queue.on_received_data_packet(payload)
        elif command == udp.CMD_ACK:
            self.file_queue.on_received_ack_packet(payload)
        if command == udp.CMD_GREETING:
            try:
                new_peer_id, new_peer_idurl = payload.split(' ')
            except:
                return
            udp.send_command(self.node.listen_port, udp.CMD_ALIVE, '', self.peer_address)
            if self.peer_id:
                if new_peer_id != self.peer_id:
                    lg.out(4, 'udp_session.doReceiveData WARNING session: %s,  peer_id from GREETING is different: %s' % (self, new_peer_id))
            else:
                # lg.out(4, 'udp_session.doReceiveData got peer id (%s) for session %s from GREETING packet' % (new_peer_id, self))
                self.peer_id = new_peer_id
            if self.peer_idurl:
                if new_peer_idurl != self.peer_idurl:
                    lg.out(4, 'udp_session.doReceiveData WARNING session: %s,  peer_idurl from GREETING is different: %s' % (self, new_peer_idurl))
            else:
                # lg.out(4, 'udp_session.doReceiveData got peer idurl (%s) for session %s from GREETING packet' % (new_peer_id, self))
                self.peer_idurl = new_peer_idurl
            for s in sessions().values():
                if self.id == s.id:
                    continue
                if self.peer_id == s.peer_id:
                    lg.out(6, 'udp_session.doReceiveData WARNING  got GREETING from another address, close session %s' % s)
                    s.automat('shutdown')
                    continue
                if self.peer_idurl == s.peer_idurl:
                    lg.out(6, 'udp_session.doReceiveData WARNING  got GREETING from another idurl, close session %s' % s)
                    s.automat('shutdown')
                    continue
        elif command == udp.CMD_PING:
            payload = str(self.node.my_id)+' '+str(self.node.my_idurl)
            udp.send_command(self.node.listen_port, udp.CMD_GREETING, payload, self.peer_address)

    def doNotifyConnected(self, arg):
        """
        Action method.
        """
        # print 'CONNECTED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'

    def doNotifyDisconnected(self, arg):
        """
        Action method.
        """
        # print 'DISCONNECTED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'

    def doCheckPendingFiles(self, arg):
        """
        Action method.
        """
        global _PendingOutboxFiles
        i = 0
        while i < len(_PendingOutboxFiles):
            filename, host, description, result_defer, single, tm = _PendingOutboxFiles.pop(i)
            if host == self.peer_id:
                self.file_queue.append_outbox_file(filename, description, result_defer, single)
            else:
                _PendingOutboxFiles.insert(i, (filename, host, description, result_defer, single, tm))
                i += 1

    def doDestroyMe(self, arg):
        """
        Action method.
        """
        self.file_queue.close()
        self.file_queue = None
        self.node = None
        sessions().pop(self.id)
        sessions_by_peer_address()[self.peer_address].remove(self)
        if len(sessions_by_peer_address()[self.peer_address]) == 0:
            sessions_by_peer_address().pop(self.peer_address)
        sessions_by_peer_id()[self.peer_id].remove(self)
        if len(sessions_by_peer_id()[self.peer_id]) == 0:
            sessions_by_peer_id()[self.peer_id].pop(self.peer_id)
        automat.objects().pop(self.index)

<commit_msg>02.08.2014 at 13:49:21<commit_after>

"""
.. module:: udp_session

BitPie.NET udp_session() Automat

EVENTS:
    * :red:`datagram-received`
    * :red:`init`
    * :red:`shutdown`
    * :red:`timer-10sec`
    * :red:`timer-1min`
    * :red:`timer-1sec`
    * :red:`timer-30sec`

"""

import time

from twisted.internet import reactor

from logs import lg

from lib import misc
from lib import automat
from lib import udp

import udp_file_queue

#------------------------------------------------------------------------------

MIN_PROCESS_SESSIONS_DELAY = 0.001
MAX_PROCESS_SESSIONS_DELAY = 1.0

#------------------------------------------------------------------------------

_SessionsDict = {}
_SessionsDictByPeerAddress = {}
_SessionsDictByPeerID = {}
_KnownPeersDict = {}
_KnownUserIDsDict = {}
_PendingOutboxFiles = []
_ProcessSessionsTask = None
_ProcessSessionsDelay = MIN_PROCESS_SESSIONS_DELAY

#------------------------------------------------------------------------------

def sessions():
    """
    """
    global _SessionsDict
    return _SessionsDict

def sessions_by_peer_address():
    global _SessionsDictByPeerAddress
    return _SessionsDictByPeerAddress

def sessions_by_peer_id():
    global _SessionsDictByPeerID
    return _SessionsDictByPeerID

def create(node, peer_address, peer_id=None):
    """
    """
    lg.out(10, 'udp_session.create peer_address=%s' % str(peer_address))
    s = UDPSession(node, peer_address, peer_id)
    sessions()[s.id] = s
    try:
        sessions_by_peer_address()[peer_address].append(s)
    except:
        sessions_by_peer_address()[peer_address] = [s,]
    try:
        sessions_by_peer_id()[peer_id].append(s)
    except:
        sessions_by_peer_id()[peer_id] = [s,]
    return s

def get(peer_address):
    """
    """
    # lg.out(18, 'udp_session.get %s %s' % (str(peer_address),
    #     str(map(lambda s:s.peer_address, sessions().values()))))
    for s in sessions_by_peer_address().get(peer_address, []):
        return s
    # for id, s in sessions().items():
    #     if s.peer_address == peer_address:
    #         return s
    return None

def get_by_peer_id(peer_id):
    """
    """
    for s in sessions_by_peer_id().get(peer_id, []):
        return s
    # for id, s in sessions().items():
    #     if s.peer_id == peer_id:
    #         return s
    return None

def close(peer_address):
    """
    """
    s = get(peer_address)
    if s is None:
        return False
    s.automat('shutdown')
    return True

def add_pending_outbox_file(filename, host, description='', result_defer=None, single=False):
    """
    """
    global _PendingOutboxFiles
    _PendingOutboxFiles.append((filename, host, description, result_defer, single, time.time()))

def process_sessions():
    global _ProcessSessionsTask
    global _ProcessSessionsDelay
    has_activity = False
    for s in sessions().values():
        if not s.file_queue:
            continue
        has_outbox = s.file_queue.process_outbox_queue()
        has_sends = s.file_queue.process_outbox_files()
        if has_sends or has_outbox:
            has_activity = True
    if has_activity:
        _ProcessSessionsTask = reactor.callLater(0, process_sessions)
    else:
        _ProcessSessionsDelay = misc.LoopAttenuation(
            _ProcessSessionsDelay, has_activity,
            MIN_PROCESS_SESSIONS_DELAY, MAX_PROCESS_SESSIONS_DELAY,)
        # attenuation
        _ProcessSessionsTask = reactor.callLater(_ProcessSessionsDelay,
                                                 process_sessions)

def stop_process_sessions(self):
    global _ProcessSessionsTask
    if _ProcessSessionsTask:
        if _ProcessSessionsTask.active():
            _ProcessSessionsTask.cancel()
            _ProcessSessionsTask = None

#------------------------------------------------------------------------------

class UDPSession(automat.Automat):
    """
    This class implements all the functionality of the ``udp_session()`` state machine.
    """

    fast = True

    timers = {
        'timer-1min':  (60,   ['CONNECTED']),
        'timer-1sec':  (1.0,  ['PING','GREETING']),
        'timer-30sec': (30.0, ['GREETING']),
        'timer-10sec': (10.0, ['PING','CONNECTED']),
        }

    def __init__(self, node, peer_address, peer_id=None):
        self.node = node
        self.peer_address = peer_address
        self.peer_id = peer_id
        self.peer_idurl = None
        self.last_datagram_received_time = 0
        self.bytes_sent = 0
        self.bytes_received = 0
        self.file_queue = udp_file_queue.FileQueue(self)
        name = 'udp_session[%s:%d]' % (self.peer_address[0], self.peer_address[1])
        automat.Automat.__init__(self, name, 'AT_STARTUP')

    def send_packet(self, command, payload):
        self.bytes_sent += len(payload)
        return udp.send_command(self.node.listen_port, command,
                                payload, self.peer_address)

    def A(self, event, arg):
        #---CONNECTED---
        if self.state == 'CONNECTED':
            if event == 'datagram-received' :
                self.doReceiveData(arg)
            elif event == 'timer-10sec' :
                self.doAlive(arg)
            elif event == 'shutdown' or ( event == 'timer-1min' and not self.isSessionActive(arg) ) :
                self.state = 'CLOSED'
                self.doNotifyDisconnected(arg)
                self.doDestroyMe(arg)
        #---AT_STARTUP---
        elif self.state == 'AT_STARTUP':
            if event == 'init' :
                self.state = 'PING'
                self.doInit(arg)
                self.doPing(arg)
            elif event == 'shutdown' :
                self.state = 'CLOSED'
                self.doDestroyMe(arg)
        #---PING---
        elif self.state == 'PING':
            if event == 'timer-1sec' :
                self.doPing(arg)
            elif event == 'datagram-received' and self.isPing(arg) :
                self.state = 'GREETING'
                self.doReceiveData(arg)
            elif event == 'datagram-received' and self.isGreeting(arg) :
                self.state = 'GREETING'
                self.doReceiveData(arg)
            elif event == 'datagram-received' and not self.isPingOrGreeting(arg) :
                self.doReceiveData(arg)
            elif event == 'shutdown' or event == 'timer-10sec' :
                self.state = 'CLOSED'
                self.doDestroyMe(arg)
        #---GREETING---
        elif self.state == 'GREETING':
            if event == 'timer-1sec' :
                self.doGreeting(arg)
            elif event == 'shutdown' or event == 'timer-30sec' :
                self.state = 'CLOSED'
                self.doDestroyMe(arg)
            elif event == 'datagram-received' and not self.isGreetingOrAlive(arg) :
                self.doReceiveData(arg)
            elif event == 'datagram-received' and self.isGreetingOrAlive(arg) :
                self.state = 'CONNECTED'
                self.doReceiveData(arg)
                self.doNotifyConnected(arg)
                self.doCheckPendingFiles(arg)
        #---CLOSED---
        elif self.state == 'CLOSED':
            pass

    def isPing(self, arg):
        """
        Condition method.
        """
        command = arg[0][0]
        return command == udp.CMD_PING

    def isGreeting(self, arg):
        """
        Condition method.
        """
        command = arg[0][0]
        return command == udp.CMD_GREETING

    def isPingOrGreeting(self, arg):
        """
        Condition method.
        """
        command = arg[0][0]
        return ( command == udp.CMD_PING or command == udp.CMD_GREETING)

    def isGreetingOrAlive(self, arg):
        """
        Condition method.
        """
        command = arg[0][0]
        return ( command == udp.CMD_ALIVE or command == udp.CMD_GREETING)

    def isSessionActive(self, arg):
        """
        Condition method.
        """
        return time.time() - self.last_datagram_received_time < 60

    def doInit(self, arg):
        """
        Action method.
        """
        # self.listen_port, self.my_id, self.my_address = arg

    def doAlive(self, arg):
        """
        Action method.
        """
        udp.send_command(self.node.listen_port, udp.CMD_ALIVE, '', self.peer_address)

    def doGreeting(self, arg):
        """
        Action method.
        """
        payload = str(self.node.my_id)+' '+str(self.node.my_idurl)
        udp.send_command(self.node.listen_port, udp.CMD_GREETING, payload, self.peer_address)

    def doPing(self, arg):
        """
        Action method.
        """
        udp.send_command(self.node.listen_port, udp.CMD_PING, '', self.peer_address)

    def doReceiveData(self, arg):
        """
        Action method.
        """
        self.last_datagram_received_time = time.time()
        try:
            datagram, address = arg
            command, payload = datagram
        except:
            return
        assert address == self.peer_address
        self.bytes_received += len(payload)
        if command == udp.CMD_DATA:
            self.file_queue.on_received_data_packet(payload)
        elif command == udp.CMD_ACK:
            self.file_queue.on_received_ack_packet(payload)
        if command == udp.CMD_GREETING:
            try:
                new_peer_id, new_peer_idurl = payload.split(' ')
            except:
                return
            udp.send_command(self.node.listen_port, udp.CMD_ALIVE, '', self.peer_address)
            if self.peer_id:
                if new_peer_id != self.peer_id:
                    lg.out(4, 'udp_session.doReceiveData WARNING session: %s,  peer_id from GREETING is different: %s' % (self, new_peer_id))
            else:
                # lg.out(4, 'udp_session.doReceiveData got peer id (%s) for session %s from GREETING packet' % (new_peer_id, self))
                self.peer_id = new_peer_id
            if self.peer_idurl:
                if new_peer_idurl != self.peer_idurl:
                    lg.out(4, 'udp_session.doReceiveData WARNING session: %s,  peer_idurl from GREETING is different: %s' % (self, new_peer_idurl))
            else:
                # lg.out(4, 'udp_session.doReceiveData got peer idurl (%s) for session %s from GREETING packet' % (new_peer_id, self))
                self.peer_idurl = new_peer_idurl
            for s in sessions().values():
                if self.id == s.id:
                    continue
                if self.peer_id == s.peer_id:
                    lg.out(6, 'udp_session.doReceiveData WARNING  got GREETING from another address, close session %s' % s)
                    s.automat('shutdown')
                    continue
                if self.peer_idurl == s.peer_idurl:
                    lg.out(6, 'udp_session.doReceiveData WARNING  got GREETING from another idurl, close session %s' % s)
                    s.automat('shutdown')
                    continue
        elif command == udp.CMD_PING:
            payload = str(self.node.my_id)+' '+str(self.node.my_idurl)
            udp.send_command(self.node.listen_port, udp.CMD_GREETING, payload, self.peer_address)

    def doNotifyConnected(self, arg):
        """
        Action method.
        """
        # print 'CONNECTED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'

    def doNotifyDisconnected(self, arg):
        """
        Action method.
        """
        # print 'DISCONNECTED!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!'

    def doCheckPendingFiles(self, arg):
        """
        Action method.
        """
        global _PendingOutboxFiles
        i = 0
        while i < len(_PendingOutboxFiles):
            filename, host, description, result_defer, single, tm = _PendingOutboxFiles.pop(i)
            if host == self.peer_id:
                self.file_queue.append_outbox_file(filename, description, result_defer, single)
            else:
                _PendingOutboxFiles.insert(i, (filename, host, description, result_defer, single, tm))
                i += 1

    def doDestroyMe(self, arg):
        """
        Action method.
        """
        self.file_queue.close()
        self.file_queue = None
        self.node = None
        sessions().pop(self.id)
        sessions_by_peer_address()[self.peer_address].remove(self)
        if len(sessions_by_peer_address()[self.peer_address]) == 0:
            sessions_by_peer_address().pop(self.peer_address)
        sessions_by_peer_id()[self.peer_id].remove(self)
        if len(sessions_by_peer_id()[self.peer_id]) == 0:
            sessions_by_peer_id().pop(self.peer_id)
        automat.objects().pop(self.index)

<|endoftext|>


<commit_before>import sys
try:
    import xml.etree.cElementTree as ET
except:
    import xml.etree.ElementTree as ET
try:
    from amanzi_xml.utils.search import *
except:
    print "Error: Unable to locate amanzi xml_functions module"
    print "Add $AMANZI_SRC_DIR/tools/amanzi_xml to your PYTHONPATH environment variable"
    sys.exit()
from copy import deepcopy
from xml.dom import minidom
import re
import sys
import numpy
from glob import glob
from subprocess import call

class ATSXML(object):
    def __init__(self,filename=None,xml_string=None):
        if filename is not None and xml_string is not None:
            print 'Error: Cannot define both filename and string!\n'
            return
        elif filename is not None:
            self.filename = filename
            self.read_xml(filename)
        elif xml_string is not None:
            self.filename = None
            self.fromstring(xml_string)
    def read_xml(self,filename):
        self.tree = ET.parse(filename)
        self.root = self.tree.getroot()
    def fromstring(self,xml_string):
        self.root = ET.fromstring(xml_string)
    @property
    def regions(self):
        return self.root.findall('ParameterList/[@name="Regions"]/ParameterList')
    @property
    def parent_map(self):
        return create_parent_map(self)
    # In amanzi_xml mesh.py
    #@property
    #def simulated_times(self):
    #    base_name = self.find_name('visualization').find('.//*/[@name="file name base"]').attrib['value']
    #    # Assume base_name+'_data*' will get the times ok, should work unless things change in ATS output
    #    base_name += '_data'
    #    ts = []
    #    for fn in glob(base_name+'.h5.*.xmf'):
    #        t = ET.parse(fn)
    #        r = t.getroot()
    #        ts.append(float(r.find('.//*/Time').attrib['Value']))
    #    return numpy.array(ts)
    def replace_regions(self,fromatsxml,mapping=None):
        '''
        Replace regions in xml with regions in another xml

        :param fromatsxml: ATSXML object with replacement regions
        :type fromatsxml: ATSXML object
        :param mapping: dictionary of replacement region names keyed by current region names
        :type mapping: dict[str] = str
        '''
        replace_regions(self,fromatsxml,mapping=mapping)
    def replace_by_value(self,oldvalue,newvalue):
        """Replace all matches of a given 'value'='oldvalue' with 'newvalue'"""
        replace_by_value(self.root,oldvalue,newvalue)
    def replace_by_name(self,name,value):
        """Replace all matches of a given 'name' with 'newvalue'"""
        replace_by_name(self.root,name,value)
    def replace_file(self,newfilename,oldfilename=None):
        if oldfilename is not None: replace_by_value(self.root,oldfilename,newfilename)
        else: replace_by_name(self.root,'file',newfilename)
    def replace_mesh_file(self,newfilename):
        for e in self.root.findall('.//ParameterList/[@name="Region: Labeled Set"]'):
            e2 = e.find('.//Parameter/[@name="file"]')
            print e2.attrib
            if e2 is not None: e2.set('value',newfilename)
            e2 = e.find('.//Parameter/[@name="File"]')
            if e2 is not None: e2.set('value',newfilename)
        for e in self.root.findall('.//ParameterList/[@name="Read Mesh File"]'):
            e2 = e.find('.//Parameter/[@name="file"]')
            if e2 is not None: e2.set('value',newfilename)
            e2 = e.find('.//Parameter/[@name="File"]')
            if e2 is not None: e2.set('value',newfilename)
    def replace_restart_file(self,filename):
        replace_by_name(self.root,'restart file', filename)
    #def replace_elems(self,fromatsxml,element_names):
    #    for nm in element_names:
    #        e = fromatsxml.root.findall(".//ParameterList[@name='"+nm+"']")[0]
    #        self.root.remove(self.root.findall(".//ParameterList[@name='"+nm+"']")[0])
    #        self.root.append(e)
    def replace_elem(self,elem_sink,elem_src):
        """Replace the element 'sink' with the element 'src' in the hierarchy 'xml'"""
        replace_elem(self.root,elem_sink,elem_src)
        #pm = self.parent_map
        #p_elem_sink = pm[elem_sink]
        #p_elem_sink.remove(elem_sink)
        #p_elem_sink.append(elem_src)
    # Now in xml_functions
    #def getvalue(self,name):
    #    out = []
    #    for r in self.root.findall('.//Parameter/[@name="'+str(name)+'"]'):
    #        out.append(r.attrib['value'])
    #    if len(out) > 1: return out
    #    else: return out[0]
    # Potentially not needed anymore, xml's written directly with write look ok now
    def write(self,filename):
        #self.tree.write(filename)
        m_str = ET.tostring(self.root)
        m_str_strip = m_str.replace('\n','')
        m_str_strip = re.sub('>\s*<','><',m_str_strip)
        m_reparsed = minidom.parseString(m_str_strip)
        with open(filename, "w") as f:
            f.write(m_reparsed.toprettyxml(indent="  "))
    #def findall_name(self,name,elem=None):
    #    if elem is None:
    #        return self.root.findall('.//*/[@name="'+str(name)+'"]')
    #    else:
    #        return elem.findall('.//*/[@name="'+str(name)+'"]')
    #def find_name(self,name,elem=None):
    #    if elem is None:
    #        elem = self.root
    #    if isinstance(name,str): name = numpy.array([name])
    #    for n in name:
    #        elem = elem.find('.//*/[@name="'+str(n)+'"]')
    #    return elem
    #def findall_value(self,value,elem=None):
    #    if elem is None:
    #        return self.root.findall('.//*/[@value="'+str(value)+'"]')
    #    else:
    #        return elem.findall('.//*/[@value="'+str(value)+'"]')
    #def find_value(self,value,elem=None):
    #    if elem is None:
    #        return self.root.find('.//*/[@value="'+str(value)+'"]')
    #    else:
    #        return elem.find('.//*/[@value="'+str(value)+'"]')
    #def findall_name_value(self,name,value,elem=None):
    #    if elem is None:
    #        return self.root.findall('.//*/[@name="'+str(name)+'"]/[@value="'+str(value)+'"]')
    #    else:
    #        return elem.findall('.//*/[@name="'+str(name)+'"]/[@value="'+str(value)+'"]')
    #def find_name_value(self,name,value,elem=None):
    #    if elem is None:
    #        return self.root.find('.//*/[@name="'+str(name)+'"]/[@value="'+str(value)+'"]')
    #    else:
    #        return elem.find('.//*/[@name="'+str(name)+'"]/[@value="'+str(value)+'"]')
    def uptree(self,elem,level=1):
        pm = self.parent_map
        elems = [elem]
        for i in range(level):
            if elem in pm:
                elems.append(pm[elem])
                elem = pm[elem]
            else:
                break
        ind = ''
        s = ''
        for e in reversed(elems[1:]):
            s += "\n"+ind+e.attrib['name']
            ind += '    '
        s += "\n"+ind+elems[0].attrib['name']
        if 'value' in elems[0].attrib:
            s += ": "+elems[0].attrib['value']
        for e in elems[1].getchildren():
            if 'value' in e.attrib and e is not elems[0]:
                s += "\n"+ind+e.attrib['name']+": "+e.attrib['value']
        print s
    def dump(self,elem=None,uplevel=0):
        if elem is None: return ET.dump(self)
        else:
            if uplevel > 0:
                pm = self.parent_map
                for i in range(uplevel): elem = pm[elem]
            ET.dump(elem)
    def remove_elem(self,elem):
        print "remove_elem deprecated, it has been replaced with remove"
    def remove(self,elem):
        """Removes the xml 'elem' wherever it is locaed in 'xml'"""
        remove(self.root,elem)
    def set_verbosity(self,value):
        '''
        Set verbosity level to value in all available Verbose Objects

        :param value: Verbosity level: high,low
        :type value: str
        '''
        for e in findall_name(self.root,'Verbosity Level'):
            e.set('value',value)
    # Now in xml_functions
    #def add_Parameter(self,name,value,vtype,elem=None):
    #    if elem is None: ET.SubElement(self.root,'Parameter',{'name':name,'type':vtype,'value':str(value)})
    #    else: ET.SubElement(elem,'Parameter',{'name':name,'type':vtype,'value':str(value)})
    #def add_ParameterList(self,name,elem=None):
    #    if elem is None: ET.SubElement(self.root,'ParameterList',{'name':name,'type':'ParameterList'})
    #    else: ET.SubElement(elem,'ParameterList',{'name':name,'type':'ParameterList'})

# Doesn't seem to get used anymore
#class Region(object):
#    def __init__(self,element):
#        self.element = element
#        self.name = element.attrib['name']
#        self.type = element.find('ParameterList').attrib['name']
#        d = {}
#        pl = element.find('ParameterList')
#        for v in pl.findall('Parameter'):
#            d[v.attrib['name']] = v.attrib['value']
#        self.dict = d
#    def __repr__(self):
#        return self.name

def get_root(filename=None,xml_string=None):
    if filename is not None and xml_string is not None:
        print 'Error: Cannot define both filename and string!\n'
        return
    elif filename is not None:
        t = ET.parse(filename)
        r = t.getroot()
    elif xml_string is not None:
        r = ET.fromstring(xml_string)
    return r
    #def read_xml(self,filename):
    #    t = ET.parse(filename)
    #    r = t.getroot()
    #def fromstring(self,xml_string):
    #    r = ET.fromstring(xml_string)

def get_regions(xml):
    return xml.findall('ParameterList/[@name="Regions"]/ParameterList')

def write_xml(xml,filename):
    #self.tree.write(filename)
    m_str = ET.tostring(xml)
    m_str_strip = m_str.replace('\n','')
    m_str_strip = re.sub('>\s*<','><',m_str_strip)
    m_reparsed = minidom.parseString(m_str_strip)
    with open(filename, "w") as f:
        f.write(m_reparsed.toprettyxml(indent="  "))

def replace_regions(toatsxml,fromatsxml,mapping=None):
    '''
    Replace regions in xml with regions in another xml

    :param toatsxml: ATSXML object to replace regions in
    :type fromatsxml: ATSXML object
    :param fromatsxml: ATSXML object with replacement regions
    :type fromatsxml: ATSXML object
    :param mapping: dictionary of replacement region names keyed by current region names
    :type mapping: dict[str] = str
    '''
    # Create mapping
    m = {}
    for r in get_regions(fromatsxml):
        m[r.attrib['name']] = r.attrib['name']
    # Override mappings with those provided
    if mapping is not None:
        for k,v in mapping.items(): m[k] = v
    # Change regions
    for rb in toatsxml.findall('.//Parameter/[@name="region"]/../..'):
        for rp in rb.findall('*/Parameter/[@name="region"]/..'):
            r = rp.find('./Parameter/[@name="region"]')
            name = r.attrib['value']
            if name in m:
                r.set('value',m[name])
            else:
                rb.remove(rp)
    for r in toatsxml.findall('.//Parameter/[@name="regions"]'):
        rs = []
        for rn in r.attrib['value'].strip('{}').split(','):
            if rn in m: rs.append(m[rn])
        #rs = [rn for rn in r.attrib['value'].strip('{}').split(',')]
        rs = '{'+','.join(rs)+'}'
        r.set('value',rs)
    for r in toatsxml.findall('.//Parameter/[@name="domain name"]'):
        r.set('value',m[r.attrib['value']])
    # Replace Mesh and Regions blocks
    replace_elem(toatsxml,toatsxml.find('./ParameterList/[@name="Regions"]'),fromatsxml.find('./ParameterList/[@name="Regions"]'))
    replace_elem(toatsxml,find_name(toatsxml,'Mesh'),find_name(fromatsxml,'Mesh'))
    newinput = get_value(toatsxml,'Native Unstructured Input')
    replace_by_name(toatsxml,'Native Unstructured Input',newinput)
    newgridoption = get_value(toatsxml,'grid_option')
    replace_by_name(toatsxml,'grid_option',newgridoption)

def run_ats(pars, tpl_file_name='../CESM8_5-implicit.xml', run_file_name='run.xml',hostname=None,processor=None):
    ''' Run ats model based on tpl_xml_file using parameters defined in pars dictionary '''

    print hostname, processor
    mpirun_exe = '/usr/projects/terraarctic/ats/openmpi/openmpi-1.8.1/wolf/gcc-4.7.2/bin/mpirun'

    # ensure that ATS's executable exists and that it's module is loaded
    try:
        path = os.path.join(os.environ['ATS_DIR'],'bin')
    except KeyError:
        raise RuntimeError("Missing ATS installation, please set the ATS_DIR environmental variable.")
    executable = os.path.join(path, "ats")
    if not os.path.isfile(executable):
        raise RuntimeError("Missing ATS installation, please build and install ATS.")

    try:
        #ierr = os.system(' '.join([mpirun_exe,"-n 1","-H",hostname,"--cpu-set",str(processor),executable,"--xml_file="+run_file_name,">&","stdout.out"]))
        ierr = call([executable,"--xml_file="+run_file_name], stdout=stdout_file, stderr= subprocess.STDOUT)
    except:
        return [ierr]

<commit_msg>Some atsxml cleanup and fixed atsxml.run<commit_after>import os,sys
try:
    import xml.etree.cElementTree as ET
except:
    import xml.etree.ElementTree as ET
try:
    from amanzi_xml.utils.search import *
except:
    print "Error: Unable to locate amanzi xml_functions module"
    print "Add $AMANZI_SRC_DIR/tools/amanzi_xml to your PYTHONPATH environment variable"
    sys.exit()
from copy import deepcopy
from xml.dom import minidom
import re
import sys
import numpy
from glob import glob
from subprocess import call

class ATSXML(object):
    def __init__(self,filename=None,xml_string=None):
        if filename is not None and xml_string is not None:
            print 'Error: Cannot define both filename and string!\n'
            return
        elif filename is not None:
            self.filename = filename
            self.read_xml(filename)
        elif xml_string is not None:
            self.filename = None
            self.fromstring(xml_string)
    def read_xml(self,filename):
        self.tree = ET.parse(filename)
        self.root = self.tree.getroot()
    def fromstring(self,xml_string):
        self.root = ET.fromstring(xml_string)
    @property
    def regions(self):
        return self.root.findall('ParameterList/[@name="Regions"]/ParameterList')
    @property
    def parent_map(self):
        return create_parent_map(self)
    def replace_regions(self,fromatsxml,mapping=None):
        '''
        Replace regions in xml with regions in another xml

        :param fromatsxml: ATSXML object with replacement regions
        :type fromatsxml: ATSXML object
        :param mapping: dictionary of replacement region names keyed by current region names
        :type mapping: dict[str] = str
        '''
        replace_regions(self,fromatsxml,mapping=mapping)
    def replace_by_value(self,oldvalue,newvalue):
        """Replace all matches of a given 'value'='oldvalue' with 'newvalue'"""
        replace_by_value(self.root,oldvalue,newvalue)
    def replace_by_name(self,name,value):
        """Replace all matches of a given 'name' with 'newvalue'"""
        replace_by_name(self.root,name,value)
    def replace_file(self,newfilename,oldfilename=None):
        if oldfilename is not None: replace_by_value(self.root,oldfilename,newfilename)
        else: replace_by_name(self.root,'file',newfilename)
    def replace_mesh_file(self,newfilename):
        for e in self.root.findall('.//ParameterList/[@name="Region: Labeled Set"]'):
            e2 = e.find('.//Parameter/[@name="file"]')
            print e2.attrib
            if e2 is not None: e2.set('value',newfilename)
            e2 = e.find('.//Parameter/[@name="File"]')
            if e2 is not None: e2.set('value',newfilename)
        for e in self.root.findall('.//ParameterList/[@name="Read Mesh File"]'):
            e2 = e.find('.//Parameter/[@name="file"]')
            if e2 is not None: e2.set('value',newfilename)
            e2 = e.find('.//Parameter/[@name="File"]')
            if e2 is not None: e2.set('value',newfilename)
    def replace_restart_file(self,filename):
        replace_by_name(self.root,'restart file', filename)
    def replace_elem(self,elem_sink,elem_src):
        """Replace the element 'sink' with the element 'src' in the hierarchy 'xml'"""
        replace_elem(self.root,elem_sink,elem_src)
    # Potentially not needed anymore, xml's written directly with write look ok now
    def write(self,filename):
        #self.tree.write(filename)
        m_str = ET.tostring(self.root)
        m_str_strip = m_str.replace('\n','')
        m_str_strip = re.sub('>\s*<','><',m_str_strip)
        m_reparsed = minidom.parseString(m_str_strip)
        with open(filename, "w") as f:
            f.write(m_reparsed.toprettyxml(indent="  "))
    #def findall_name(self,name,elem=None):
    #    if elem is None:
    #        return self.root.findall('.//*/[@name="'+str(name)+'"]')
    #    else:
    #        return elem.findall('.//*/[@name="'+str(name)+'"]')
    #def find_name(self,name,elem=None):
    #    if elem is None:
    #        elem = self.root
    #    if isinstance(name,str): name = numpy.array([name])
    #    for n in name:
    #        elem = elem.find('.//*/[@name="'+str(n)+'"]')
    #    return elem
    #def findall_value(self,value,elem=None):
    #    if elem is None:
    #        return self.root.findall('.//*/[@value="'+str(value)+'"]')
    #    else:
    #        return elem.findall('.//*/[@value="'+str(value)+'"]')
    #def find_value(self,value,elem=None):
    #    if elem is None:
    #        return self.root.find('.//*/[@value="'+str(value)+'"]')
    #    else:
    #        return elem.find('.//*/[@value="'+str(value)+'"]')
    #def findall_name_value(self,name,value,elem=None):
    #    if elem is None:
    #        return self.root.findall('.//*/[@name="'+str(name)+'"]/[@value="'+str(value)+'"]')
    #    else:
    #        return elem.findall('.//*/[@name="'+str(name)+'"]/[@value="'+str(value)+'"]')
    #def find_name_value(self,name,value,elem=None):
    #    if elem is None:
    #        return self.root.find('.//*/[@name="'+str(name)+'"]/[@value="'+str(value)+'"]')
    #    else:
    #        return elem.find('.//*/[@name="'+str(name)+'"]/[@value="'+str(value)+'"]')
    def uptree(self,elem,level=1):
        pm = self.parent_map
        elems = [elem]
        for i in range(level):
            if elem in pm:
                elems.append(pm[elem])
                elem = pm[elem]
            else:
                break
        ind = ''
        s = ''
        for e in reversed(elems[1:]):
            s += "\n"+ind+e.attrib['name']
            ind += '    '
        s += "\n"+ind+elems[0].attrib['name']
        if 'value' in elems[0].attrib:
            s += ": "+elems[0].attrib['value']
        for e in elems[1].getchildren():
            if 'value' in e.attrib and e is not elems[0]:
                s += "\n"+ind+e.attrib['name']+": "+e.attrib['value']
        print s
    def dump(self,elem=None,uplevel=0):
        if elem is None: return ET.dump(self)
        else:
            if uplevel > 0:
                pm = self.parent_map
                for i in range(uplevel): elem = pm[elem]
            ET.dump(elem)
    def remove_elem(self,elem):
        print "remove_elem deprecated, it has been replaced with remove"
    def remove(self,elem):
        """Removes the xml 'elem' wherever it is locaed in 'xml'"""
        remove(self.root,elem)
    def set_verbosity(self,value):
        '''
        Set verbosity level to value in all available Verbose Objects

        :param value: Verbosity level: high,low
        :type value: str
        '''
        for e in findall_name(self.root,'Verbosity Level'):
            e.set('value',value)
    # Now in amanzi_xml.utils.search
    #def add_Parameter(self,name,value,vtype,elem=None):
    #    if elem is None: ET.SubElement(self.root,'Parameter',{'name':name,'type':vtype,'value':str(value)})
    #    else: ET.SubElement(elem,'Parameter',{'name':name,'type':vtype,'value':str(value)})
    #def add_ParameterList(self,name,elem=None):
    #    if elem is None: ET.SubElement(self.root,'ParameterList',{'name':name,'type':'ParameterList'})
    #    else: ET.SubElement(elem,'ParameterList',{'name':name,'type':'ParameterList'})

#class Region(object):
#    def __init__(self,element):
#        self.element = element
#        self.name = element.attrib['name']
#        self.type = element.find('ParameterList').attrib['name']
#        d = {}
#        pl = element.find('ParameterList')
#        for v in pl.findall('Parameter'):
#            d[v.attrib['name']] = v.attrib['value']
#        self.dict = d
#    def __repr__(self):
#        return self.name

def get_root(filename=None,xml_string=None):
    if filename is not None and xml_string is not None:
        print 'Error: Cannot define both filename and string!\n'
        return
    elif filename is not None:
        t = ET.parse(filename)
        r = t.getroot()
    elif xml_string is not None:
        r = ET.fromstring(xml_string)
    return r

def get_regions(xml):
    return xml.findall('ParameterList/[@name="Regions"]/ParameterList')

def write_xml(xml,filename):
    #self.tree.write(filename)
    m_str = ET.tostring(xml)
    m_str_strip = m_str.replace('\n','')
    m_str_strip = re.sub('>\s*<','><',m_str_strip)
    m_reparsed = minidom.parseString(m_str_strip)
    with open(filename, "w") as f:
        f.write(m_reparsed.toprettyxml(indent="  "))

def replace_regions(toatsxml,fromatsxml,mapping=None):
    '''
    Replace regions in xml with regions in another xml

    :param toatsxml: ATSXML object to replace regions in
    :type fromatsxml: ATSXML object
    :param fromatsxml: ATSXML object with replacement regions
    :type fromatsxml: ATSXML object
    :param mapping: dictionary of replacement region names keyed by current region names
    :type mapping: dict[str] = str
    '''
    # Create mapping
    m = {}
    for r in get_regions(fromatsxml):
        m[r.attrib['name']] = r.attrib['name']
    # Override mappings with those provided
    if mapping is not None:
        for k,v in mapping.items(): m[k] = v
    # Change regions
    for rb in toatsxml.findall('.//Parameter/[@name="region"]/../..'):
        for rp in rb.findall('*/Parameter/[@name="region"]/..'):
            r = rp.find('./Parameter/[@name="region"]')
            name = r.attrib['value']
            if name in m:
                r.set('value',m[name])
            else:
                rb.remove(rp)
    for r in toatsxml.findall('.//Parameter/[@name="regions"]'):
        rs = []
        for rn in r.attrib['value'].strip('{}').split(','):
            if rn in m: rs.append(m[rn])
        #rs = [rn for rn in r.attrib['value'].strip('{}').split(',')]
        rs = '{'+','.join(rs)+'}'
        r.set('value',rs)
    for r in toatsxml.findall('.//Parameter/[@name="domain name"]'):
        r.set('value',m[r.attrib['value']])
    # Replace Mesh and Regions blocks
    replace_elem(toatsxml,toatsxml.find('./ParameterList/[@name="Regions"]'),fromatsxml.find('./ParameterList/[@name="Regions"]'))
    replace_elem(toatsxml,find_name(toatsxml,'Mesh'),find_name(fromatsxml,'Mesh'))
    newinput = get_value(toatsxml,'Native Unstructured Input')
    replace_by_name(toatsxml,'Native Unstructured Input',newinput)
    newgridoption = get_value(toatsxml,'grid_option')
    replace_by_name(toatsxml,'grid_option',newgridoption)

def run(xml, nproc=1, mpiexec='mpiexec', run_file_name='run.xml',stdout=None,stderr=None,hostname=None,processor=None):
    ''' Run ats model based on tpl_xml_file using parameters defined in pars dictionary '''

    # ensure that ATS's executable exists and that it's module is loaded
    try:
        path = os.path.join(os.environ['ATS_DIR'],'bin')
    except KeyError:
        raise RuntimeError("Missing ATS installation, please set the ATS_DIR environmental variable.")
    executable = os.path.join(path, "ats")
    if not os.path.isfile(executable):
        raise RuntimeError("Missing ATS installation, please build and install ATS.")

    write_xml(xml,run_file_name)

    try:
        #print ' '.join([mpiexec,'-n',str(nproc),executable,"--xml_file="+run_file_name])
        outfile = open(stdout,'w')
        if not stderr is None:
            errfile = open(stderr,'w')
        ierr = call([mpiexec,'-n',str(nproc),executable,"--xml_file="+run_file_name],stdout=outfile,stderr=errfile)
        print ierr
    except:
        print "Error: ATS simulation failed"

<|endoftext|>


<commit_before>from pyethereum import tester
from datetime import datetime, date

import pytest
slow = pytest.mark.slow

class TestBtcTx(object):

    CONTRACT = 'tryStoreHeaders.py'
    CONTRACT_GAS = 55000

    ETHER = 10 ** 18

    def setup_class(cls):
        cls.s = tester.state()
        cls.c = cls.s.abi_contract(cls.CONTRACT, endowment=2000*cls.ETHER)
        cls.snapshot = cls.s.snapshot()
        cls.seed = tester.seed

    def setup_method(self, method):
        self.s.revert(self.snapshot)
        tester.seed = self.seed

    @slow
    # @pytest.mark.skipif(True,reason='skip')
    def test2(self):
        heaviest = 5
        self.c.initAncestorDepths()

        for i in range(1, heaviest+1):
          self.c.testStoreB(i, i, i-1)
        self.c.testSetHeaviest(heaviest)

        self.c.testStoreB(30, 30, 2)
        self.c.testStoreB(31, 31, 30)
        self.c.testStoreB(32, 32, 31)

        res = self.c.inMainChain(3)
        print('RES: '+str(res))

        # for i in range(1, heaviest+1):
        #     self.c.logAnc(i)

        # # self.c.logAnc(63)
        # # self.c.logAnc(64)
        # # self.c.logAnc(65)
        # # self.c.logAnc(66)
        #
        # self.c.logBlockchainHead()
<commit_msg>testShortFork passes<commit_after>from pyethereum import tester
from datetime import datetime, date

import pytest
slow = pytest.mark.slow

class TestBtcTx(object):

    CONTRACT = 'tryStoreHeaders.py'
    CONTRACT_GAS = 55000

    ETHER = 10 ** 18

    def setup_class(cls):
        cls.s = tester.state()
        cls.c = cls.s.abi_contract(cls.CONTRACT, endowment=2000*cls.ETHER)
        cls.snapshot = cls.s.snapshot()
        cls.seed = tester.seed

    def setup_method(self, method):
        self.s.revert(self.snapshot)
        tester.seed = self.seed

    # @pytest.mark.skipif(True,reason='skip')
    def testShortFork(self):
        heaviest = 5
        self.c.initAncestorDepths()

        for i in range(1, heaviest+1):
            self.c.testStoreB(i, i, i-1)
        self.c.testSetHeaviest(heaviest)

        self.c.testStoreB(30, 30, 2)
        self.c.testStoreB(31, 31, 30)
        self.c.testStoreB(32, 32, 31)

        for i in range(1, heaviest+1):
            assert self.c.inMainChain(i) == [1]

        assert self.c.inMainChain(30) == [0]
        assert self.c.inMainChain(31) == [0]
        assert self.c.inMainChain(32) == [0]

        # for i in range(1, heaviest+1):
        #     self.c.logAnc(i)

        # # self.c.logAnc(63)
        # # self.c.logAnc(64)
        # # self.c.logAnc(65)
        # # self.c.logAnc(66)
        #
        # self.c.logBlockchainHead()
<|endoftext|>