<commit_before># -*- coding: utf-8 -*-
# Copyright (c) 2014 The Pycroft Authors. See the AUTHORS file.
# This file is part of the Pycroft project and licensed under the terms of
# the Apache License, Version 2.0. See the LICENSE file for details.
"""
    web.blueprints.finance
    ~~~~~~~~~~~~~~

    This module defines view functions for /finance

    :copyright: (c) 2012 by AG DSN.
"""
from itertools import imap, groupby, izip_longest, ifilter

from flask import Blueprint, render_template, redirect, url_for, jsonify,\
    request, flash, abort
from sqlalchemy import func, desc, or_
from web.blueprints.navigation import BlueprintNavigation
from forms import SemesterCreateForm, JournalEntryEditForm, JournalImportForm, \
    JournalCreateForm, FinanceAccountCreateForm
from pycroft.lib import finance, config
from datetime import datetime, timedelta
from pycroft.model.finance import Semester, Journal, JournalEntry, Split
from pycroft.model.session import session
from pycroft.model.user import User
from pycroft.model.finance import FinanceAccount, Transaction
import os
import math
from web.blueprints.access import BlueprintAccess

bp = Blueprint('finance', __name__, )
access = BlueprintAccess(bp, ['finance_show'])
nav = BlueprintNavigation(bp, "Finanzen", blueprint_access=access)

@bp.route('/')
@bp.route('/journals')
@bp.route('/journals/list')
@access.require('finance_show')
@nav.navigate(u"Journals")
def journals_list():
    journals = Journal.q.all()

    page = request.args.get("page", 1, type=int)
    limit = request.args.get("limit", 25, type=int)
    order = request.args.get("order", "transaction_date", type=str)

    count = JournalEntry.q.filter(
        JournalEntry.transaction_id == None
    ).count()

    page_count = int(math.ceil(count/float(limit)))

    offset = (page-1)*limit

    journal_entries_list = JournalEntry.q.filter(
        JournalEntry.transaction_id == None
    ).order_by(order).offset(offset).limit(limit)

    return render_template('finance/journals_list.html',
                           journals=journals,
                           journal_entries=journal_entries_list,
                           page=page,
                           page_count=page_count,
                           limit = limit)

@bp.route('/journals/import', methods=['GET', 'POST'])
@access.require('finance_change')
@nav.navigate(u"Buchungen importieren")
def journals_import():
    form = JournalImportForm()
    if form.validate_on_submit():
        try:
            finance.import_journal_csv(form.csv_file.data)
            flash(u"Der CSV-Import war erfolgreich!", "success")
        except Exception as error:
            message = u"Der CSV-Import ist fehlgeschlagen: {0}"
            flash(message.format(error.message), "error")

    return render_template('finance/journals_import.html', form=form)

@bp.route('/journals/create', methods=['GET', 'POST'])
@access.require('finance_change')
def journals_create():
    form = JournalCreateForm()

    if form.validate_on_submit():
        new_journal = Journal(
            name=form.name.data,
            bank=form.bank.data,
            account_number=form.account_number.data,
            routing_number=form.routing_number.data,
            iban=form.iban.data,
            bic=form.bic.data,
            hbci_url=form.hbci_url.data)
        session.add(new_journal)
        session.commit()
        return redirect(url_for('.journals'))

    return render_template('finance/journals_create.html',
                           form=form, page_title=u"Journal erstellen")

@bp.route('/journals/<int:journal_id>/entries/<int:entry_id>', methods=["GET", "POST"])
@access.require('finance_change')
def journals_entries_edit(journal_id, entry_id):
    entry = JournalEntry.q.get(entry_id)
    form = JournalEntryEditForm(obj=entry, journal_name=entry.journal.name)

    if form.validate():
        debit_account = entry.journal.finance_account
        credit_account = FinanceAccount.q.filter(
            FinanceAccount.id == form.finance_account_id.data
        ).one()
        entry.transaction = finance.simple_transaction(
            description=entry.description, debit_account=debit_account,
            credit_account=credit_account, amount=entry.amount,
            author=current_user, valid_date=entry.valid_date)
        entry.description = form.description.data
        session.add(entry)
        session.commit()

        return redirect(url_for('.journals'))

    return render_template(
        'finance/journals_entries_edit.html',
        entry=entry, form=form
    )

@bp.route('/accounts')
@bp.route('/accounts/list')
@nav.navigate(u"Konten")
@access.require('finance_show')
def accounts_list():
    accounts_by_type = dict(imap(
        lambda t: (t[0], list(t[1])),
        groupby(
            FinanceAccount.q .outerjoin(User).filter(User.id == None)
            .order_by(FinanceAccount.type).all(),
            lambda a: a.type
        )
    ))
    return render_template(
        'finance/accounts_list.html', accounts=accounts_by_type
    )

@bp.route('/accounts/<int:account_id>')
@access.require('finance_show')
def accounts_show(account_id):
    account = FinanceAccount.q.filter(FinanceAccount.id == account_id).one()
    splits = (
        Split.q
        .join(Transaction)
        .filter(Split.account_id == account_id)
        .order_by(Transaction.valid_date)
    )
    typed_splits = izip_longest(
        ifilter(lambda s: s.amount > 0, splits),
        ifilter(lambda s: s.amount <= 0, splits)
    )
    balance = sum(imap(lambda s: s.amount, splits))
    return render_template(
        'finance/accounts_show.html',
        account=account, balance=balance,
        splits=splits, typed_splits=typed_splits
    )

@bp.route('/transactions/<int:transaction_id>')
@access.require('finance_show')
def transactions_show(transaction_id):
    transaction = Transaction.q.get(transaction_id)
    if transaction is None:
        abort(404)
    return render_template(
        'finance/transactions_show.html',
        transaction=transaction
    )

@bp.route('/accounts/create', methods=['GET', 'POST'])
@access.require('finance_change')
def accounts_create():
    form = FinanceAccountCreateForm()

    if form.validate_on_submit():
        new_account = FinanceAccount(name=form.name.data, type=form.type.data)
        session.add(new_account)
        session.commit()
        return redirect(url_for('.accounts'))

    return render_template('finance/accounts_create.html', form=form,
                           page_title=u"Konto erstellen")

@bp.route("/semesters")
@access.require('finance_show')
@nav.navigate(u"Semesterliste")
def semesters_list():
    semesters = Semester.q.order_by(Semester.begin_date.desc()).all()
    return render_template('finance/semesters_list.html', semesters=semesters)

@bp.route('/semesters/create', methods=("GET", "POST"))
@access.require('finance_change')
def semesters_create():
    previous_semester = Semester.q.order_by(Semester.begin_date.desc()).first()
    if previous_semester:
        begin_date_default = previous_semester.end_date + timedelta(1)
        end_date_default = previous_semester.begin_date.replace(
            year=previous_semester.begin_date.year + 1
        ) - timedelta(1)
        premature_begin_date_default = begin_date_default - timedelta(30)
        belated_end_date_default = end_date_default + timedelta(30)
        if begin_date_default.year == end_date_default.year:
            name_default = u'Sommersemester ' + str(begin_date_default.year)
        else:
            name_default = (u'Wintersemester ' + str(begin_date_default.year) +
                            u'/' + str(end_date_default.year))
        form = SemesterCreateForm(
            name=name_default,
            registration_fee=previous_semester.registration_fee,
            regular_semester_contribution=previous_semester.regular_semester_contribution,
            reduced_semester_contribution=previous_semester.reduced_semester_contribution,
            overdue_fine=previous_semester.overdue_fine,
            premature_begin_date=premature_begin_date_default,
            begin_date=begin_date_default,
            end_date=end_date_default,
            belated_end_date=belated_end_date_default)
    else:
        form = SemesterCreateForm()
    if form.validate_on_submit():
        Semester(
            name=form.name.data,
            registration_fee=form.registration_fee.data,
            regular_semester_contribution=form.regular_semester_contribution.data,
            reduced_semester_contribution=form.reduced_semester_contribution.data,
            overdue_fine=form.overdue_fine.data,
            premature_begin_date=form.premature_begin_date.data,
            begin_date=form.begin_date.data,
            end_date=form.end_date.data,
            belated_end_date=form.belated_end_date.data
        )
        return redirect(url_for(".semesters_list"))
    return render_template('finance/semesters_create.html', form=form)

@bp.route('/json/accounts/system')
@access.require('finance_show')
def json_accounts_system():
    return jsonify(accounts=map(
        lambda account: {
            "account_id": account.id,
            "account_name": account.name
        },
        session.query(FinanceAccount).outerjoin(User).filter(
            User.finance_account == None
        ).all()
    ))

@bp.route('/json/accounts/user-search')
@access.require('finance_show')
def json_accounts_user_search():
    query = request.args['query']
    return jsonify(accounts=map(
        lambda result: {
            "account_id": result[0],
            "user_id": result[1],
            "user_name": result[2]
        },
        session.query(FinanceAccount.id, User.id, User.name).filter(
            FinanceAccount.id == User.finance_account_id,
            or_(
                func.lower(User.name).like(func.lower("%{0}%".format(query))),
                User.id.like("{0}%".format(query))
            )
        ).all()
    ))
<commit_msg>Reorder imports<commit_after># -*- coding: utf-8 -*-
# Copyright (c) 2014 The Pycroft Authors. See the AUTHORS file.
# This file is part of the Pycroft project and licensed under the terms of
# the Apache License, Version 2.0. See the LICENSE file for details.
"""
    web.blueprints.finance
    ~~~~~~~~~~~~~~

    This module defines view functions for /finance

    :copyright: (c) 2012 by AG DSN.
"""
from datetime import timedelta
from itertools import (imap, groupby, izip_longest, ifilter)
import math

from flask import (
    Blueprint, render_template, redirect, url_for, jsonify,
    request, flash, abort)
from flask.ext.login import current_user
from sqlalchemy import func, or_

from web.blueprints.finance.forms import (
    SemesterCreateForm, JournalEntryEditForm, JournalImportForm,
    JournalCreateForm, FinanceAccountCreateForm)
from pycroft.lib import finance
from pycroft.model.finance import Semester, Journal, JournalEntry, Split
from pycroft.model.session import session
from pycroft.model.user import User
from pycroft.model.finance import FinanceAccount, Transaction
from web.blueprints.access import BlueprintAccess
from web.blueprints.navigation import BlueprintNavigation

bp = Blueprint('finance', __name__, )
access = BlueprintAccess(bp, ['finance_show'])
nav = BlueprintNavigation(bp, "Finanzen", blueprint_access=access)

@bp.route('/')
@bp.route('/journals')
@bp.route('/journals/list')
@access.require('finance_show')
@nav.navigate(u"Journals")
def journals_list():
    journals = Journal.q.all()

    page = request.args.get("page", 1, type=int)
    limit = request.args.get("limit", 25, type=int)
    order = request.args.get("order", "transaction_date", type=str)

    count = JournalEntry.q.filter(
        JournalEntry.transaction_id == None
    ).count()

    page_count = int(math.ceil(count/float(limit)))

    offset = (page-1)*limit

    journal_entries_list = JournalEntry.q.filter(
        JournalEntry.transaction_id == None
    ).order_by(order).offset(offset).limit(limit)

    return render_template('finance/journals_list.html',
                           journals=journals,
                           journal_entries=journal_entries_list,
                           page=page,
                           page_count=page_count,
                           limit = limit)

@bp.route('/journals/import', methods=['GET', 'POST'])
@access.require('finance_change')
@nav.navigate(u"Buchungen importieren")
def journals_import():
    form = JournalImportForm()
    if form.validate_on_submit():
        try:
            finance.import_journal_csv(form.csv_file.data)
            flash(u"Der CSV-Import war erfolgreich!", "success")
        except Exception as error:
            message = u"Der CSV-Import ist fehlgeschlagen: {0}"
            flash(message.format(error.message), "error")

    return render_template('finance/journals_import.html', form=form)

@bp.route('/journals/create', methods=['GET', 'POST'])
@access.require('finance_change')
def journals_create():
    form = JournalCreateForm()

    if form.validate_on_submit():
        new_journal = Journal(
            name=form.name.data,
            bank=form.bank.data,
            account_number=form.account_number.data,
            routing_number=form.routing_number.data,
            iban=form.iban.data,
            bic=form.bic.data,
            hbci_url=form.hbci_url.data)
        session.add(new_journal)
        session.commit()
        return redirect(url_for('.journals'))

    return render_template('finance/journals_create.html',
                           form=form, page_title=u"Journal erstellen")

@bp.route('/journals/<int:journal_id>/entries/<int:entry_id>', methods=["GET", "POST"])
@access.require('finance_change')
def journals_entries_edit(journal_id, entry_id):
    entry = JournalEntry.q.get(entry_id)
    form = JournalEntryEditForm(obj=entry, journal_name=entry.journal.name)

    if form.validate():
        debit_account = entry.journal.finance_account
        credit_account = FinanceAccount.q.filter(
            FinanceAccount.id == form.finance_account_id.data
        ).one()
        entry.transaction = finance.simple_transaction(
            description=entry.description, debit_account=debit_account,
            credit_account=credit_account, amount=entry.amount,
            author=current_user, valid_date=entry.valid_date)
        entry.description = form.description.data
        session.add(entry)
        session.commit()

        return redirect(url_for('.journals'))

    return render_template(
        'finance/journals_entries_edit.html',
        entry=entry, form=form
    )

@bp.route('/accounts')
@bp.route('/accounts/list')
@nav.navigate(u"Konten")
@access.require('finance_show')
def accounts_list():
    accounts_by_type = dict(imap(
        lambda t: (t[0], list(t[1])),
        groupby(
            FinanceAccount.q .outerjoin(User).filter(User.id == None)
            .order_by(FinanceAccount.type).all(),
            lambda a: a.type
        )
    ))
    return render_template(
        'finance/accounts_list.html', accounts=accounts_by_type
    )

@bp.route('/accounts/<int:account_id>')
@access.require('finance_show')
def accounts_show(account_id):
    account = FinanceAccount.q.filter(FinanceAccount.id == account_id).one()
    splits = (
        Split.q
        .join(Transaction)
        .filter(Split.account_id == account_id)
        .order_by(Transaction.valid_date)
    )
    typed_splits = izip_longest(
        ifilter(lambda s: s.amount > 0, splits),
        ifilter(lambda s: s.amount <= 0, splits)
    )
    balance = sum(imap(lambda s: s.amount, splits))
    return render_template(
        'finance/accounts_show.html',
        account=account, balance=balance,
        splits=splits, typed_splits=typed_splits
    )

@bp.route('/transactions/<int:transaction_id>')
@access.require('finance_show')
def transactions_show(transaction_id):
    transaction = Transaction.q.get(transaction_id)
    if transaction is None:
        abort(404)
    return render_template(
        'finance/transactions_show.html',
        transaction=transaction
    )

@bp.route('/accounts/create', methods=['GET', 'POST'])
@access.require('finance_change')
def accounts_create():
    form = FinanceAccountCreateForm()

    if form.validate_on_submit():
        new_account = FinanceAccount(name=form.name.data, type=form.type.data)
        session.add(new_account)
        session.commit()
        return redirect(url_for('.accounts'))

    return render_template('finance/accounts_create.html', form=form,
                           page_title=u"Konto erstellen")

@bp.route("/semesters")
@access.require('finance_show')
@nav.navigate(u"Semesterliste")
def semesters_list():
    semesters = Semester.q.order_by(Semester.begin_date.desc()).all()
    return render_template('finance/semesters_list.html', semesters=semesters)

@bp.route('/semesters/create', methods=("GET", "POST"))
@access.require('finance_change')
def semesters_create():
    previous_semester = Semester.q.order_by(Semester.begin_date.desc()).first()
    if previous_semester:
        begin_date_default = previous_semester.end_date + timedelta(1)
        end_date_default = previous_semester.begin_date.replace(
            year=previous_semester.begin_date.year + 1
        ) - timedelta(1)
        premature_begin_date_default = begin_date_default - timedelta(30)
        belated_end_date_default = end_date_default + timedelta(30)
        if begin_date_default.year == end_date_default.year:
            name_default = u'Sommersemester ' + str(begin_date_default.year)
        else:
            name_default = (u'Wintersemester ' + str(begin_date_default.year) +
                            u'/' + str(end_date_default.year))
        form = SemesterCreateForm(
            name=name_default,
            registration_fee=previous_semester.registration_fee,
            regular_semester_contribution=previous_semester.regular_semester_contribution,
            reduced_semester_contribution=previous_semester.reduced_semester_contribution,
            overdue_fine=previous_semester.overdue_fine,
            premature_begin_date=premature_begin_date_default,
            begin_date=begin_date_default,
            end_date=end_date_default,
            belated_end_date=belated_end_date_default)
    else:
        form = SemesterCreateForm()
    if form.validate_on_submit():
        Semester(
            name=form.name.data,
            registration_fee=form.registration_fee.data,
            regular_semester_contribution=form.regular_semester_contribution.data,
            reduced_semester_contribution=form.reduced_semester_contribution.data,
            overdue_fine=form.overdue_fine.data,
            premature_begin_date=form.premature_begin_date.data,
            begin_date=form.begin_date.data,
            end_date=form.end_date.data,
            belated_end_date=form.belated_end_date.data
        )
        return redirect(url_for(".semesters_list"))
    return render_template('finance/semesters_create.html', form=form)

@bp.route('/json/accounts/system')
@access.require('finance_show')
def json_accounts_system():
    return jsonify(accounts=map(
        lambda account: {
            "account_id": account.id,
            "account_name": account.name
        },
        session.query(FinanceAccount).outerjoin(User).filter(
            User.finance_account == None
        ).all()
    ))

@bp.route('/json/accounts/user-search')
@access.require('finance_show')
def json_accounts_user_search():
    query = request.args['query']
    return jsonify(accounts=map(
        lambda result: {
            "account_id": result[0],
            "user_id": result[1],
            "user_name": result[2]
        },
        session.query(FinanceAccount.id, User.id, User.name).filter(
            FinanceAccount.id == User.finance_account_id,
            or_(
                func.lower(User.name).like(func.lower("%{0}%".format(query))),
                User.id.like("{0}%".format(query))
            )
        ).all()
    ))
<|endoftext|>


<commit_before>#! /usr/bin/env ccp4-python

import argparse
import os
import pickle
import sys

from mrbump.cluster import refmacEXE
from mrbump.building import MRBUMP_Shelxe
from mrbump.building import MRBUMP_Buccaneer
from mrbump.building import MRBUMP_ARPwARP
from mrbump.parsers import parse_buccaneer
from mrbump.parsers import parse_arpwarp
from mrbump.tools import MRBUMP_phs2mtz, make_dictionary

from mrbump.structures.Matches import Match_struct, SEQ_match, Process_Score
from mrbump.structures.Model_struct import MR_setup
from mrbump.file_info.MRBUMP_target_info import TargetInfo
from mrbump.modelling.Model import Modelling
from mrbump.tools.make_dictionary import makeSearchDict
from mrbump.initialisation.MRBUMP_initialise import Initialise

class ShelxePlusRebuild:
    """Run shelxe and rebuild"""

    def __init__(self):
        self.logfile = ""
        self.DB_fail = False
        if os.name == "nt":
            self.pdbcurEXE = os.path.join(os.environ["CCP4_BIN"], "pdbcur.exe")
            self.refmacEXE = os.path.join(os.environ["CCP4_BIN"], "refmac5.exe")
        else:
            self.pdbcurEXE = os.path.join(os.environ["CCP4_BIN"], "pdbcur")
            self.refmacEXE = os.path.join(os.environ["CCP4_BIN"], "refmac5")
        try:
            self.debug = eval(os.environ['MRBUMP_DEBUG'])
        except:
            self.debug = False

    def shelxe_trace(self, init, mstat, model, target_info):

        shelxe_job = MRBUMP_Shelxe.Shelxe()
        #shelxe_dir = os.path.join(model.model_directory, "mr", "phaser", "build", "shelxe")
        shelxe_dir = os.path.join(model.model_directory, "shelxe")
        shelxe_job.setShelxWorkingDIR(shelxe_dir)
        shelxe_job.setShelxeLogFile(os.path.join(shelxe_dir, "shelxe_run.log"))
        shelxe_PDBINfile = model.refmac_phaser_PDBfile
        shelxe_MTZINfile = model.refmac_phaser_MTZINfile
        # Run SHELXE to do phase improvement and a c-alpha trace
        # Build up list of argument and keyword arguments
        shelxe_args = [target_info.solvent, target_info.resolution, "PHASER", shelxe_PDBINfile, shelxe_MTZINfile]
        shelxe_kwargs = {
            'pdboutFile': model.shelxe_phaser_PDBfile,
            'phsoutFile': model.shelxe_phaser_PHSfile,
            'traceCycles': init.keywords.SCYCLES,
            'fp': init.workingData.mtz_coldata['F'],
            'sigfp': init.workingData.mtz_coldata['SIGF'],
            'free': init.workingData.mtz_coldata['FREE'],
            'shelxeEXE': init.keywords.SHLXEXE
        }
        if not init.keywords.PDBNATIVE is None:
            shelxe_kwargs['native'] = init.keywords.PDBNATIVE

        shelxe_job.runShelxe(*shelxe_args, **shelxe_kwargs)

        # Convert the phs file to an mtz
        if os.path.isfile(model.shelxe_phaser_PHSfile) and os.path.isfile(model.shelxe_phaser_PDBfile):
            p2m = MRBUMP_phs2mtz.PHS2MTZ()
            p2m.phs2mtz(
                model.shelxe_phaser_PHSfile,
                model.shelxe_phaser_PDBfile,
                model.shelxe_phaser_MTZfile,
                shelxe_job.workingDIR,
                hklref=init.workingData.hklin,
                f=init.workingData.mtz_coldata['F'],
                sigf=init.workingData.mtz_coldata['SIGF'],
                freeLabel=init.workingData.mtz_coldata['FREE'],
                resolution=target_info.resolution,
                debug=self.debug)
        else:
            sys.stdout.write("%s: warning: no PHS file found for SHELXE (after phaser):\n  %s" %
                             (model.name, model.shelxe_phaser_PHSfile))

        # Set the SHELXE CC value and average chain length for partial model
        shelxe_job.parseLog()
        cc = shelxe_job.resultsDict['SHELXE_CC']
        acl = shelxe_job.resultsDict['SHELXE_ACL']
        if cc and acl:  # Only set if found as are initialised to 0.0
            model.shelxe_phaser_CCscore = cc
            model.shelxe_phaser_AvgChainLen = acl
        mstat.results_dict[model.name]["PHASER"]["SHELXE_CC"] = cc
        mstat.results_dict[model.name]["PHASER"]["SHELXE_ACL"] = acl
        mstat.results_dict[model.name]["PHASER"]["SHELXE_MCL"] = shelxe_job.resultsDict['SHELXE_MCL']
        mstat.results_dict[model.name]["PHASER"]["SHELXE_NC"] = shelxe_job.resultsDict['SHELXE_NC']
        mstat.results_dict[model.name]["PHASER"]["SHELXE_time"] = shelxe_job.resultsDict['SHELXE_time']
        mstat.results_dict[model.name]["PHASER"]["SHELXE_version"] = shelxe_job.resultsDict['SHELXE_version']
        mstat.results_dict[model.name]["PHASER"]["SHELXE_wMPE"] = shelxe_job.resultsDict['SHELXE_wMPE']
        mstat.results_dict[model.name]["PHASER"]["SHELXE_os"] = shelxe_job.resultsDict['SHELXE_os']

        #sys.stdout.write("%s: warning: no log file found for SHELXE (after phaser):\n  %s" % (model.name, model.shelxe_phaser_logfile))

        return shelxe_job

    def rebuild_buccaneer(self, init, mstat, model, target_info, rebuild_dir, refmacSX_MTZout, refmacSX_PDBout):
        # Run Buccaneer to build on the refined SHELXE trace
        buccaneerSX = MRBUMP_Buccaneer.Buccaneer()
        os.mkdir(os.path.join(rebuild_dir, "buccaneer"))  # Set the output MTZ and PDB file from Buccaneer
        model.buccSHELXE_phaser_PDBfile = os.path.join(rebuild_dir, "buccaneer", "buccSX_output.pdb")
        model.buccSHELXE_phaser_MTZfile = os.path.join(rebuild_dir, "buccaneer", "buccaneer_pipeline", "refine.mtz")
        buccSHELXE_phaser_logfile = os.path.join(rebuild_dir, "buccaneer", "buccaneer.log")
        # Run Buccaneer
        if init.keywords.USEPHS:
            buccaneerSX.runBuccaneer(
                init.seqin,
                model.shelxe_phaser_MTZfile,
                model.buccSHELXE_phaser_PDBfile,
                os.path.join(rebuild_dir, "buccaneer"),
                'F',
                'SIGF',
                init.workingData.mtz_coldata['FREE'],
                "PHI_SHELXE",
                "FOM_SHELXE",
                cycles=5,
                pdbinFile=model.shelxe_phaser_PDBfile,
                WEB_PATH_START=init.WEB_PATH_START)
        else:
            buccaneerSX.runBuccaneer(
                init.seqin,
                refmacSX_MTZout,
                model.buccSHELXE_phaser_PDBfile,
                os.path.join(rebuild_dir, "buccaneer"),
                init.workingData.mtz_coldata['F'],
                init.workingData.mtz_coldata['SIGF'],
                init.workingData.mtz_coldata['FREE'],
                "PHIC",
                "FOM",
                "FWT",
                "PHWT",
                cycles=5,
                pdbinFile=refmacSX_PDBout,
                WEB_PATH_START=init.WEB_PATH_START)

        # Add the bits to the results dictionary
        mstat.results_dict[model.name]["PHASER"]["SXRBUCC_pdbout"] = model.buccSHELXE_phaser_PDBfile
        mstat.results_dict[model.name]["PHASER"]["SXRBUCC_mtzout"] = model.buccSHELXE_phaser_MTZfile
        if os.path.isfile(buccSHELXE_phaser_logfile):
            mstat.results_dict[model.name]["PHASER"]["SXRBUCC_logfile"] = buccSHELXE_phaser_logfile
            bp = parse_buccaneer.BuccaneerLogParser(buccSHELXE_phaser_logfile)
            mstat.results_dict[model.name]["PHASER"]["SXRBUCC_version"] = bp.version
            mstat.results_dict[model.name]["PHASER"]["SXRBUCC_final_Rfact"] = bp.finalRfact
            mstat.results_dict[model.name]["PHASER"]["SXRBUCC_final_Rfree"] = bp.finalRfree
        return

    def rebuild_arpwarp(self, init, mstat, model, target_info, rebuild_dir, refmacSX_MTZout, refmacSX_PDBout):
        # Run ARP/wARP to build on the refined SHELXE trace
        arpwarpSX = MRBUMP_ARPwARP.Arpwarp()
        os.mkdir(os.path.join(rebuild_dir, "arpwarp"))
        # Set the output MTZ and PDB file from ARP/wARP
        if init.keywords.USEPHS:
            model.arpSHELXE_phaser_PDBfile = os.path.join(
                rebuild_dir, "arpwarp",
                "%s_warpNtrace.pdb" % os.path.splitext(os.path.split(model.shelxe_phaser_MTZfile)[1])[0])
            model.arpSHELXE_phaser_MTZfile = os.path.join(
                rebuild_dir, "arpwarp",
                "%s_warpNtrace.mtz" % os.path.splitext(os.path.split(model.shelxe_phaser_MTZfile)[1])[0])
        else:
            model.arpSHELXE_phaser_PDBfile = os.path.join(rebuild_dir, "arpwarp", "refmacSX_output_warpNtrace.pdb")
            model.arpSHELXE_phaser_MTZfile = os.path.join(rebuild_dir, "arpwarp", "refmacSX_output_warpNtrace.mtz")
        arpSHELXE_phaser_logfile = os.path.join(rebuild_dir, "arpwarp", "arpwarp.log")
        # Set the number of residues in the asu
        noResiduesASU = target_info.no_of_res * target_info.no_of_mols
        # Run ARP/wARP
        if init.keywords.USEPHS:
            arpwarpSX.runARPwARP(
                init.seqin,
                model.shelxe_phaser_MTZfile,
                os.path.join(rebuild_dir, "arpwarp"),
                'F',
                'SIGF',
                init.workingData.mtz_coldata['FREE'],
                "PHI_SHELXE",
                "FOM_SHELXE",
                noResiduesASU,
                cycles=5,
                pdbinFile=model.shelxe_phaser_PDBfile)
        else:
            arpwarpSX.runARPwARP(
                init.seqin,
                refmacSX_MTZout,
                os.path.join(rebuild_dir, "arpwarp"),
                init.workingData.mtz_coldata['F'],
                init.workingData.mtz_coldata['SIGF'],
                init.workingData.mtz_coldata['FREE'],
                "PHIC",
                "FOM",
                noResiduesASU,
                cycles=5,
                pdbinFile=refmacSX_PDBout)
        # Add the bits to the results dictionary
        mstat.results_dict[model.name]["PHASER"]["SXRARP_pdbout"] = model.arpSHELXE_phaser_PDBfile
        mstat.results_dict[model.name]["PHASER"]["SXRARP_mtzout"] = model.arpSHELXE_phaser_MTZfile
        if os.path.isfile(arpSHELXE_phaser_logfile):
            mstat.results_dict[model.name]["PHASER"]["SXRARP_logfile"] = arpSHELXE_phaser_logfile
            ap = parse_arpwarp.ArpwarpLogParser(arpSHELXE_phaser_logfile)
            mstat.results_dict[model.name]["PHASER"]["SXRARP_version"] = ap.version
            mstat.results_dict[model.name]["PHASER"]["SXRARP_final_Rfact"] = ap.finalRfact
            mstat.results_dict[model.name]["PHASER"]["SXRARP_final_Rfree"] = ap.finalRfree
        return

    def submit_serial(self, init, mstat, model, target_info):
        #=================================================================================#
        #================== Run MR and refinement on the local machine ===================#
        #=================================================================================#

        ###############################################################################
        ############################# Running Phaser ##################################
        ###############################################################################
        self.logfile = os.path.join(model.model_directory, "phaser_refmac_job.log")

        # C-alpha tracing with Shelxe
        shelxe_job = self.shelxe_trace(init, mstat, model, target_info)
        if not init.keywords.SXREBUILD:
            return

        # Rebuilding the shelxe trace
        if not os.path.isfile(model.shelxe_phaser_PDBfile) or not os.path.isfile(model.shelxe_phaser_PHSfile):
            sys.stdout.write("Warning: No output PDB from SHELXE, can't run SHELXE rebuild step\n")
            sys.stdout.write("\n")
            return

        if not shelxe_job.succeeded():
            sys.stdout.write("Warning: SHELXE trace didn't pass success criteria so not continuing with rebuilding\n")
            sys.stdout.write("\n")
            return

        # Directory where all rebuilding is carried out
        rebuild_dir = os.path.join(shelxe_job.workingDIR, "rebuild")
        os.mkdir(rebuild_dir)

        # Take the SHELXE result and use it as a starting point in Buccaneer and/or ARP/wARP for building the target
        refmacSX_MTZout = None
        refmacSX_PDBout = None
        if not init.keywords.USEPHS:
            # If we are using the model file from SHELXE for phase information we need to run refmac on it
            # Run a round of refinement and model building with Buccaneer post SHELXE
            os.mkdir(os.path.join(rebuild_dir, "refine"))
            # Setup a Refmac run
            refmacSX_job = refmacEXE.RefmacEXE()
            # Setup the Refmac keywords
            refmacSX_job.add_keyword("LABIN FP=" + init.workingData.mtz_coldata['F'] + " SIGFP=" + init.workingData.
                                     mtz_coldata['SIGF'] + " FREE=" + init.workingData.mtz_coldata['FREE'])
            refmacSX_job.add_keyword('ridg DIST SIGM 0.02')
            refmacSX_job.add_keyword('weight auto')
            refmacSX_job.add_keyword('ncyc 30')
            refmacSX_job.add_keyword('make hydr no')
            refmacSX_job.add_keyword('END')
            # Set the output and log files for Refmac
            refmacSX_MTZout = os.path.join(rebuild_dir, "refine", "refmacSX_output.mtz")
            refmacSX_PDBout = os.path.join(rebuild_dir, "rebuild", "refine", "refmacSX_output.pdb")
            refmacSX_Logfile = os.path.join(rebuild_dir, "rebuild", "refine", "refmacSX.log")
            # Run refmac Restrained Refinement with Jelly Body
            refmacSX_job.run(
                model.refmac_phaser_MTZINfile,
                refmacSX_MTZout,
                model.shelxe_phaser_PDBfile,
                refmacSX_PDBout,
                refmacSX_Logfile,
                "Restrained Refinement with Jelly Body",
                refineDir=os.path.join(rebuild_dir, "refine"),
                script=os.path.join(rebuild_dir, "refine", "refmacSX_run_script.sh"),
                LITE=False,
                WEB_PATH_START=init.WEB_PATH_START)

        if init.keywords.SXRBUCC:
            self.rebuild_buccaneer(init, mstat, model, target_info, rebuild_dir, refmacSX_MTZout, refmacSX_PDBout)

        if init.keywords.SXRARPW:
            self.rebuild_arpwarp(init, mstat, model, target_info, rebuild_dir, refmacSX_MTZout, refmacSX_PDBout)
        return

def setup(search_dir):
    mrbump = os.path.join(os.environ["CCP4"], "share", "mrbump")
    mrbump_incl = os.path.join(mrbump, "include")
    mrbump_data = os.path.join(mrbump, "data")

    os.environ['MRBUMP_DEBUG'] = "True"
    os.environ['MRBUMP_CLUSTER'] = "False"
    os.environ['PYTHONUNBUFFERED'] = "True"
    os.environ["GFORTRAN_UNBUFFERED_ALL"] = "Y"
    os.environ["MRBUMP_CLUSTBIN"] = os.path.join(mrbump_incl, "cluster")
    if 'CCP4_BIN' not in os.environ:
        os.environ['CCP4_BIN'] = os.path.join(os.environ['CCP4'], "bin")
    if os.path.isdir(os.path.join(os.environ['CCP4'], 'libexec')) and os.name=="nt":
        os.environ['PATH'] = os.environ['PATH'] + os.pathsep + os.path.join(os.environ["CCP4"], "libexec")

    init = Initialise()
    init.setMRBUMP(mrbump)
    init.setMRBUMP_INCL(mrbump_incl)
    init.setMRBUMP_DATA(mrbump_data)
    init.search_dir = os.path.abspath(search_dir)
    init.search_dict = makeSearchDict(search_dir)
    init.make_job_dir()

    mstat = Match_struct()
    mstat.search_dir = init.search_dir
    mstat.results_dir = init.results_dir

    # Need to make sure we don't try and write out an I2 report
    def mock_makeI2report(*args):
        pass
    mstat.makeI2report = mock_makeI2report

    return init, mstat

parser = argparse.ArgumentParser()
parser.add_argument('--name')
parser.add_argument('--resolution', type=float)
parser.add_argument('--seqin')
parser.add_argument('--hklin')
parser.add_argument('--pdb_native')
parser.add_argument('--f_label')
parser.add_argument('--sigf_label')
parser.add_argument('--free_label')
parser.add_argument('--refmac_mtz')
parser.add_argument('--refmac_pdb')

args = parser.parse_args()
# Put everything from the command-line into local variables
locals().update(vars(args))

# Main
search_dir = os.path.join(os.getcwd(), name)
init, mstat = setup(search_dir)

# Global keywords
init.keywords.SXREBUILD = True
init.keywords.SXRBUCC = True

# Specify info on target
target_info = TargetInfo()
# target_info.seqfile = 'FIXME'
# target_info.sequence = 'FIXME'
# target_info.mol_weight = 7071.09
# target_info.no_of_mols = 1
# target_info.solvent =
target_info.resolution = resolution

init.workingData.mtz_coldata['F'] = f_label
init.workingData.mtz_coldata['SIGF'] = sigf_label
init.workingData.mtz_coldata['FREE'] = free_label
init.workingData.hklin = hklin
init.keywords.PDBNATIVE = pdb_native
init.seqin = seqin

model = Modelling()
model.name = name
model.model_directory = os.path.join(init.search_dir, model.name)
mstat.results_dict[model.name] = make_dictionary.makeDict(init.search_dir)
shelxe_dir = os.path.join(model.model_directory, "shelxe")
os.makedirs(shelxe_dir)

model.refmac_phaser_PDBfile = refmac_pdb
model.refmac_phaser_MTZINfile = refmac_mtz
model.shelxe_phaser_PDBfile = os.path.join(shelxe_dir  ,"shelxe_phaser_" + model.name + ".pdb")
model.shelxe_phaser_PHSfile = os.path.join(shelxe_dir  ,"shelxe_phaser_" + model.name + ".phs")
model.shelxe_phaser_MTZfile = os.path.join(shelxe_dir  ,"shelxe_phaser_" + model.name + ".mtz")

spr = ShelxePlusRebuild()
spr.submit_serial(init, mstat, model, target_info)

init.resultspkl = os.path.join(init.search_dir, "results", "resultsTable.pkl")
with open(os.path.join(init.resultspkl), "w") as f:
    pickle.dump(mstat.results_dict, f)

<commit_msg>Removed as now in ample-scripts repository<commit_after><|endoftext|>


<commit_before>import datetime
import re
from subprocess import check_output
import webbrowser

import click

#from blackbelt.apis.trello import *
from blackbelt.config import config

from .apis.trello import Trello as TrelloApi

__all__ = ("schedule_list", "migrate_label", "schedule_list")

STORY_CARD_TODO_LIST_NAMES = [
    "To Do",
    "ToDo",
    "Engineering ToDo"
]

TODO_QUEUE_NAME = "To Do Queue"
DEPLOY_QUEUE_NAME = "Ready"
DEPLOYED_PREFIX = "Deployed by"
VERIFIED_PREFIX = "Verified by"

def get_token_url():
    return TrelloApi().get_token_url("black-belt")

def get_api():
    return TrelloApi()

def get_column(name, board_id=None):
    api = get_api()

    if not board_id:
        board_id = config['trello']['work_board_id']

    columns = api.get_columns(board_id)
    column = None

    for col in columns:
        if col['name'] == name:
            column = col

    if not column:
        raise ValueError("Cannot find column %s" % name)

    return column

def get_next_todo_card():
    api = get_api()

    column = get_column(name=TODO_QUEUE_NAME)
    cards = api.get_cards(column_id=column['id'])

    me = api.get_myself()

    my_cards = [card for card in cards if me['id'] in card['idMembers']]

    if len(my_cards) < 1:
        raise ValueError("No card assigned to you in the To Do Queue -- be happy, your job is done!")

    return my_cards[0]

def get_current_working_ticket(card_url=None):
    api = get_api()

    column = get_column(name=config['trello']['work_column_name'])

    cards = api.get_cards(column_id=column['id'])

    me = api.get_myself()

    my_cards = [card for card in cards if me['id'] in card['idMembers']]
    work_card = None

    if len(my_cards) < 1:
        raise ValueError("No working card in your DOING list; aborting.")

    if not card_url:

        if len(my_cards) == 1:
            work_card = my_cards[0]

        if len(my_cards) > 1:
            for card in my_cards:
                if len(card['idMembers']) == 1:
                    if not work_card:
                        work_card = card
                    else:
                        raise ValueError("Multiple available work cards; Try bb gh pr <card_url> ")
    else:
        url_cards = [card for card in my_cards if card['url'] == card_url]

        if len(url_cards) > 0:
            work_card = url_cards[0]

    if not work_card:
        raise ValueError("The specified card_url is not in your DOING list")

    return work_card

def open_current_working_ticket():
    ticket = get_current_working_ticket()
    webbrowser.open(ticket['url'])

def get_ticket_ready(ticket):
    api = get_api()
    column = get_column(name=DEPLOY_QUEUE_NAME)
    api.move_card(card_id=ticket['id'], column_id=column['id'])

def comment_ticket(ticket, comment):
    api = get_api()
    api.comment_card(card_id=ticket['id'], comment=comment)

def migrate_label(label, board, board_to, column, column_to):
    api = get_api()

    if column:
        raise ValueError("column is now ignored, you need to program support for it")

    board_info = api.get_board(board_id=board)

    final_label = None

    if label in board_info['labelNames'].keys():
        final_label = label
    else:
        for l in board_info['labelNames']:
            if board_info['labelNames'][l] == label:
                final_label = l

    if not final_label:
        raise ValueError("Cannot find label %s on given board")

    cards = api.get_cards(board_id=board)

    filtered_cards = []

    for c in cards:
        for l in c['labels']:
            if l['color'] == final_label:
                filtered_cards.append(c)

    board_to_info = api.get_board(board_id=board_to)
    board_to_columns = api.get_columns(board_id=board_to_info['id'])

    for c in board_to_columns:
        if c['name'] == column_to:
            target_column = c

    if not target_column:
        raise ValueError("Cannot find target column %s" % column_to)

    for card in filtered_cards:
        print("Moving card %(id)s: %(name)s" % card)
        api.move_card(
            card_id=card['id'],
            board_id=target_column['idBoard'],
            list_id=target_column['id']
        )

def get_conversion_items(api, card_list, story_card, story_list):
    """ Return (todo_list, items_to_convert_from_there) """
    todo_list = None

    for item in card_list:
        if story_list:
            if item['name'] == story_list or item['id'] == story_list:
                todo_list = item
                break
        else:
            for name in STORY_CARD_TODO_LIST_NAMES:
                if item['name'].lower().strip() == name.strip().lower():
                    todo_list = item
                    break

    if not todo_list:
        lists = ', '.join([i['name'] for i in card_list])
        raise ValueError("Cannot find checklist to convert. Please provide a correct --story-list parameter. Available lists are: %s" % lists)

    list_items = api.get_checklist_items(checklist_id=todo_list['id'])
    return (todo_list, [c for c in list_items if c['state'] == 'incomplete' and not c['name'].startswith('https://trello.com/c/')])

def schedule_list(story_card, story_list=None, owner=None, label=None):
    """
    Looks for Story Card, finds a list and migrate all non-card items to card,
    replacing the items with links to them.

    Work cards contain "Part of <parent-card-link>".
    """

    api = get_api()

    match = re.match("^https\:\/\/trello\.com\/c\/(?P<id>\w+)$", story_card)
    if match:
        story_card = match.groupdict()['id']

    story_card = api.get_card(card_url=story_card)
    #FIXME: list vs. lists
    card_list = api.get_card_checklists(card_id=story_card['id'])

    if owner:
        owner = api.get_member(member_name=owner)

    work_queue = get_column(TODO_QUEUE_NAME)

    todo_list, conversion_items = get_conversion_items(api, card_list, story_card, story_list)

    for item in conversion_items:
        desc = "Part of %(url)s" % story_card
        card = api.create_card(
            name=item['name'],
            description=desc,
            list_id=work_queue['id']
        )

        api.create_item(checklist_id=todo_list['id'], name=card['url'], pos=item['pos'])

        api.delete_checklist_item(checklist_id=todo_list['id'], checklist_item_id=item['id'])

        if owner:
            api.add_card_member(card_id=card['id'], member_id=owner['id'])

        if label:
            api.label_card(card_id=card['id'], label=label)

    print "Done"

def infer_branch_name(url):
    return '-'.join(url.split('/')[~0].split('-')[1:])

def next_card():
    card = get_next_todo_card()

    ticket_urlname = infer_branch_name(card['url'])

    # set up local branch
    from .handle_github import get_username, get_current_branch

    prefix = get_username().lower()

    branch_name = prefix + '/' + ticket_urlname

    if get_current_branch() != 'master':
        check_output(['git', 'checkout', 'master'])

    check_output(['git', 'pull'])

    # You think you might do git checkout branch origin/branch?
    # Oh my, silly you. All of those require the origin branch to exists,
    # therefore pushing. We don't want to push at this stage only to trigger
    # a duplicate CI check on what is basically a master.
    # Therefore, time for some dark magic. If it breaks your git, please
    # carry on and sacrifice a chicken to the daemons of Linux/s

    check_output(['git', 'branch', branch_name, 'origin/master'])
    check_output(['git', 'checkout', branch_name])
    check_output(['git', 'config', 'branch."%s".remote' % branch_name, 'origin'])
    check_output(['git', 'config', 'branch."%s".merge' % branch_name, "refs/heads/\"%s\"" % branch_name])
    check_output(['git', 'config', 'branch."%s".rebase' % branch_name, 'true'])

    # move to Doing
    api = get_api()
    api.move_card(
        card_id=card['id'],
        column_id=get_column(name=config['trello']['work_column_name'])['id']
    )

    # open card for review
    webbrowser.open(card['url'])

def move_to_deployed(card_id, comment=None):
    """ If the card is in Ready column, move it to deployed """
    api = get_api()

    column = get_column(name=DEPLOY_QUEUE_NAME)
    card = api.get_card(card_id=card_id)

    if card['idList'] != column['id']:
        print "The card is not in column %(column)s. NOT moving to Deployed for you." % {
            'column': DEPLOY_QUEUE_NAME
        }
    else:

        columns = api.get_columns(board_id=config['trello']['work_board_id'])
        deployed = None

        for col in columns:
            if col['name'].startswith(DEPLOYED_PREFIX):
                deployed = col
                # Use the first "Deployed by" as the old ones are
                # sometimes "trailing" at the end of the board
                break

        if not column:
            print "Can't find \"Deployed by\" column, NOT moving the card for you"
        else:
            api.move_card(card_id=card_id, column_id=deployed['id'])
            if comment:
                api.comment_card(card_id=card_id, comment=comment)

def next_week():
    sunday = datetime.date.today() + datetime.timedelta(days=datetime.date.today().weekday() + 6)
    sunday = sunday.isoformat()

    api = get_api()
    api.add_column(
        board_id=config['trello']['work_board_id'],
        name="Deployed by %s" % sunday,
        position=5
    )
    api.add_column(
        board_id=config['trello']['work_board_id'],
        name="Verified by %s" % sunday,
        position=6
    )

def verify(story_card):
    api = get_api()

    story_card = api.get_card(card_url=story_card)
    checklists = api.get_card_checklists(card_id=story_card['id'])

    # We actually don't care how the checklists are structured, only about the card links
    checklist_cards = []
    index = -1
    for l in checklists:
        index += 1
        for list_item in l['checkItems']:
            # We accept the first link in the checklist item
            match = re.match("^.*https://trello.com/c/(?P<id>\w+)/?(.*)", list_item['name'])
            if match:
                if list_item['state'] == u'incomplete':
                    list_item['_black_belt'] = {
                        'card_id': match.groupdict()['id'],
                        'checklist_index': index,  # Not needed now, but preserved for future list updates
                        'checklist_id': l['id']
                    }
                    checklist_cards.append(list_item)

                elif list_item['state'] != u'complete':
                    print "Unknown checklist state %s, skipping" % list_item['state']

    # We have list of cards to check, go through them and discover whether
    # they are Deployed / Verified
    COLUMN_CACHE = {}

    items_done = []
    for item in checklist_cards:
        work_card = api.get_card(card_id=item['_black_belt']['card_id'])
        column_id = work_card['idList']

        if column_id not in COLUMN_CACHE:
            COLUMN_CACHE[column_id] = api.get_column(column_id)

        name = COLUMN_CACHE[column_id]['name']
        if name.startswith(DEPLOYED_PREFIX) or name.startswith(VERIFIED_PREFIX):
            item['_black_belt']['name'] = work_card['name']
            item['_black_belt']['url'] = work_card['url']
            items_done.append(item)
        else:
            pass
            #print "%s still not done" % work_card['name'].encode('utf-8')

    # Go through the done items, ask for review and ask for completing them
    for item in items_done:
        if click.confirm("Do you want to show card %s?" % item['_black_belt']['name'], default=True):
            webbrowser.open(item['_black_belt']['url'])
        if click.confirm("Do you want to check it as verified?"):
            api.check_item(
                checklist_id=item['_black_belt']['checklist_id'],
                item_id=item['id'],
                card_id=story_card['id']
            )
<commit_msg>[Trello] Use `git fetch` to fetch origin's state<commit_after>import datetime
import re
from subprocess import check_output
import webbrowser

import click

#from blackbelt.apis.trello import *
from blackbelt.config import config

from .apis.trello import Trello as TrelloApi

__all__ = ("schedule_list", "migrate_label", "schedule_list")

STORY_CARD_TODO_LIST_NAMES = [
    "To Do",
    "ToDo",
    "Engineering ToDo"
]

TODO_QUEUE_NAME = "To Do Queue"
DEPLOY_QUEUE_NAME = "Ready"
DEPLOYED_PREFIX = "Deployed by"
VERIFIED_PREFIX = "Verified by"

def get_token_url():
    return TrelloApi().get_token_url("black-belt")

def get_api():
    return TrelloApi()

def get_column(name, board_id=None):
    api = get_api()

    if not board_id:
        board_id = config['trello']['work_board_id']

    columns = api.get_columns(board_id)
    column = None

    for col in columns:
        if col['name'] == name:
            column = col

    if not column:
        raise ValueError("Cannot find column %s" % name)

    return column

def get_next_todo_card():
    api = get_api()

    column = get_column(name=TODO_QUEUE_NAME)
    cards = api.get_cards(column_id=column['id'])

    me = api.get_myself()

    my_cards = [card for card in cards if me['id'] in card['idMembers']]

    if len(my_cards) < 1:
        raise ValueError("No card assigned to you in the To Do Queue -- be happy, your job is done!")

    return my_cards[0]

def get_current_working_ticket(card_url=None):
    api = get_api()

    column = get_column(name=config['trello']['work_column_name'])

    cards = api.get_cards(column_id=column['id'])

    me = api.get_myself()

    my_cards = [card for card in cards if me['id'] in card['idMembers']]
    work_card = None

    if len(my_cards) < 1:
        raise ValueError("No working card in your DOING list; aborting.")

    if not card_url:

        if len(my_cards) == 1:
            work_card = my_cards[0]

        if len(my_cards) > 1:
            for card in my_cards:
                if len(card['idMembers']) == 1:
                    if not work_card:
                        work_card = card
                    else:
                        raise ValueError("Multiple available work cards; Try bb gh pr <card_url> ")
    else:
        url_cards = [card for card in my_cards if card['url'] == card_url]

        if len(url_cards) > 0:
            work_card = url_cards[0]

    if not work_card:
        raise ValueError("The specified card_url is not in your DOING list")

    return work_card

def open_current_working_ticket():
    ticket = get_current_working_ticket()
    webbrowser.open(ticket['url'])

def get_ticket_ready(ticket):
    api = get_api()
    column = get_column(name=DEPLOY_QUEUE_NAME)
    api.move_card(card_id=ticket['id'], column_id=column['id'])

def comment_ticket(ticket, comment):
    api = get_api()
    api.comment_card(card_id=ticket['id'], comment=comment)

def migrate_label(label, board, board_to, column, column_to):
    api = get_api()

    if column:
        raise ValueError("column is now ignored, you need to program support for it")

    board_info = api.get_board(board_id=board)

    final_label = None

    if label in board_info['labelNames'].keys():
        final_label = label
    else:
        for l in board_info['labelNames']:
            if board_info['labelNames'][l] == label:
                final_label = l

    if not final_label:
        raise ValueError("Cannot find label %s on given board")

    cards = api.get_cards(board_id=board)

    filtered_cards = []

    for c in cards:
        for l in c['labels']:
            if l['color'] == final_label:
                filtered_cards.append(c)

    board_to_info = api.get_board(board_id=board_to)
    board_to_columns = api.get_columns(board_id=board_to_info['id'])

    for c in board_to_columns:
        if c['name'] == column_to:
            target_column = c

    if not target_column:
        raise ValueError("Cannot find target column %s" % column_to)

    for card in filtered_cards:
        print("Moving card %(id)s: %(name)s" % card)
        api.move_card(
            card_id=card['id'],
            board_id=target_column['idBoard'],
            list_id=target_column['id']
        )

def get_conversion_items(api, card_list, story_card, story_list):
    """ Return (todo_list, items_to_convert_from_there) """
    todo_list = None

    for item in card_list:
        if story_list:
            if item['name'] == story_list or item['id'] == story_list:
                todo_list = item
                break
        else:
            for name in STORY_CARD_TODO_LIST_NAMES:
                if item['name'].lower().strip() == name.strip().lower():
                    todo_list = item
                    break

    if not todo_list:
        lists = ', '.join([i['name'] for i in card_list])
        raise ValueError("Cannot find checklist to convert. Please provide a correct --story-list parameter. Available lists are: %s" % lists)

    list_items = api.get_checklist_items(checklist_id=todo_list['id'])
    return (todo_list, [c for c in list_items if c['state'] == 'incomplete' and not c['name'].startswith('https://trello.com/c/')])

def schedule_list(story_card, story_list=None, owner=None, label=None):
    """
    Looks for Story Card, finds a list and migrate all non-card items to card,
    replacing the items with links to them.

    Work cards contain "Part of <parent-card-link>".
    """

    api = get_api()

    match = re.match("^https\:\/\/trello\.com\/c\/(?P<id>\w+)$", story_card)
    if match:
        story_card = match.groupdict()['id']

    story_card = api.get_card(card_url=story_card)
    #FIXME: list vs. lists
    card_list = api.get_card_checklists(card_id=story_card['id'])

    if owner:
        owner = api.get_member(member_name=owner)

    work_queue = get_column(TODO_QUEUE_NAME)

    todo_list, conversion_items = get_conversion_items(api, card_list, story_card, story_list)

    for item in conversion_items:
        desc = "Part of %(url)s" % story_card
        card = api.create_card(
            name=item['name'],
            description=desc,
            list_id=work_queue['id']
        )

        api.create_item(checklist_id=todo_list['id'], name=card['url'], pos=item['pos'])

        api.delete_checklist_item(checklist_id=todo_list['id'], checklist_item_id=item['id'])

        if owner:
            api.add_card_member(card_id=card['id'], member_id=owner['id'])

        if label:
            api.label_card(card_id=card['id'], label=label)

    print "Done"

def infer_branch_name(url):
    return '-'.join(url.split('/')[~0].split('-')[1:])

def next_card():
    card = get_next_todo_card()

    ticket_urlname = infer_branch_name(card['url'])

    # set up local branch
    from .handle_github import get_username, get_current_branch

    prefix = get_username().lower()

    branch_name = prefix + '/' + ticket_urlname

    if get_current_branch() != 'master':
        check_output(['git', 'checkout', 'master'])

    check_output(['git', 'fetch', 'origin'])

    # You think you might do git checkout branch origin/branch?
    # Oh my, silly you. All of those require the origin branch to exists,
    # therefore pushing. We don't want to push at this stage only to trigger
    # a duplicate CI check on what is basically a master.
    # Therefore, time for some dark magic. If it breaks your git, please
    # carry on and sacrifice a chicken to the daemons of Linux/s

    check_output(['git', 'branch', branch_name, 'origin/master'])
    check_output(['git', 'checkout', branch_name])
    check_output(['git', 'config', 'branch."%s".remote' % branch_name, 'origin'])
    check_output(['git', 'config', 'branch."%s".merge' % branch_name, "refs/heads/\"%s\"" % branch_name])
    check_output(['git', 'config', 'branch."%s".rebase' % branch_name, 'true'])

    # move to Doing
    api = get_api()
    api.move_card(
        card_id=card['id'],
        column_id=get_column(name=config['trello']['work_column_name'])['id']
    )

    # open card for review
    webbrowser.open(card['url'])

def move_to_deployed(card_id, comment=None):
    """ If the card is in Ready column, move it to deployed """
    api = get_api()

    column = get_column(name=DEPLOY_QUEUE_NAME)
    card = api.get_card(card_id=card_id)

    if card['idList'] != column['id']:
        print "The card is not in column %(column)s. NOT moving to Deployed for you." % {
            'column': DEPLOY_QUEUE_NAME
        }
    else:

        columns = api.get_columns(board_id=config['trello']['work_board_id'])
        deployed = None

        for col in columns:
            if col['name'].startswith(DEPLOYED_PREFIX):
                deployed = col
                # Use the first "Deployed by" as the old ones are
                # sometimes "trailing" at the end of the board
                break

        if not column:
            print "Can't find \"Deployed by\" column, NOT moving the card for you"
        else:
            api.move_card(card_id=card_id, column_id=deployed['id'])
            if comment:
                api.comment_card(card_id=card_id, comment=comment)

def next_week():
    sunday = datetime.date.today() + datetime.timedelta(days=datetime.date.today().weekday() + 6)
    sunday = sunday.isoformat()

    api = get_api()
    api.add_column(
        board_id=config['trello']['work_board_id'],
        name="Deployed by %s" % sunday,
        position=5
    )
    api.add_column(
        board_id=config['trello']['work_board_id'],
        name="Verified by %s" % sunday,
        position=6
    )

def verify(story_card):
    api = get_api()

    story_card = api.get_card(card_url=story_card)
    checklists = api.get_card_checklists(card_id=story_card['id'])

    # We actually don't care how the checklists are structured, only about the card links
    checklist_cards = []
    index = -1
    for l in checklists:
        index += 1
        for list_item in l['checkItems']:
            # We accept the first link in the checklist item
            match = re.match("^.*https://trello.com/c/(?P<id>\w+)/?(.*)", list_item['name'])
            if match:
                if list_item['state'] == u'incomplete':
                    list_item['_black_belt'] = {
                        'card_id': match.groupdict()['id'],
                        'checklist_index': index,  # Not needed now, but preserved for future list updates
                        'checklist_id': l['id']
                    }
                    checklist_cards.append(list_item)

                elif list_item['state'] != u'complete':
                    print "Unknown checklist state %s, skipping" % list_item['state']

    # We have list of cards to check, go through them and discover whether
    # they are Deployed / Verified
    COLUMN_CACHE = {}

    items_done = []
    for item in checklist_cards:
        work_card = api.get_card(card_id=item['_black_belt']['card_id'])
        column_id = work_card['idList']

        if column_id not in COLUMN_CACHE:
            COLUMN_CACHE[column_id] = api.get_column(column_id)

        name = COLUMN_CACHE[column_id]['name']
        if name.startswith(DEPLOYED_PREFIX) or name.startswith(VERIFIED_PREFIX):
            item['_black_belt']['name'] = work_card['name']
            item['_black_belt']['url'] = work_card['url']
            items_done.append(item)
        else:
            pass
            #print "%s still not done" % work_card['name'].encode('utf-8')

    # Go through the done items, ask for review and ask for completing them
    for item in items_done:
        if click.confirm("Do you want to show card %s?" % item['_black_belt']['name'], default=True):
            webbrowser.open(item['_black_belt']['url'])
        if click.confirm("Do you want to check it as verified?"):
            api.check_item(
                checklist_id=item['_black_belt']['checklist_id'],
                item_id=item['id'],
                card_id=story_card['id']
            )
<|endoftext|>


<commit_before># coding=utf8

import copy, os
import wx._core as wx
import blogtrans
from wx.html import HtmlWindow

from BlogTreeCtrl import BlogTreeCtrl
from BlogHtmlCtrl import BlogHtmlWindow
from ProgressDialog import *

from blogtrans.data import *
from blogtrans.wretch.WretchImporter import WretchImporter
from blogtrans.mt import *

from blogtrans.blogger.BloggerExporter import *
from blogtrans.blogger.BloggerImporter import *

from blogtrans.ui.BlogHtmlCtrl import CommentToHTML

ID_IMPORT_WRETCH = wx.NewId()
ID_IMPORT_BLOGGER = wx.NewId()
ID_IMPORT_MT = wx.NewId()

ID_EXPORT_BLOGGER = wx.NewId()
ID_EXPORT_MT = wx.NewId()

ID_TOOL_COMBINE_COMMENT = wx.NewId()

class MainWindow(wx.Frame):

    def __init_menubar(self) :

        #Todo: use a smarter way to manage menu...
        import_menu = wx.Menu()

        import_menu.Append(ID_IMPORT_WRETCH, u"無名XML檔案(&W)...",u"匯入無名XML檔案")
        wx.EVT_MENU(self, ID_IMPORT_WRETCH, self.OnImportWretch)

        # import_menu.Append(ID_IMPORT_BLOGGER, u"Blogger Atom XML(&B)...",u"匯入Blogger Atom XML")
        # wx.EVT_MENU(self, ID_IMPORT_BLOGGER, self.OnImportBlogger)

        import_menu.Append(ID_IMPORT_MT, u"&MT檔案...",u"匯入MT Import檔案")
        wx.EVT_MENU(self, ID_IMPORT_MT, self.OnImportMT)

        export_menu = wx.Menu()
        export_menu.Append(ID_EXPORT_BLOGGER, u"Blogger Atom XML(&B)...",u"匯出至Blogger Atom XML")
        wx.EVT_MENU(self, ID_EXPORT_BLOGGER, self.OnExportBlogger)

        export_menu.Append(ID_EXPORT_MT, u"&MT檔案...",u"匯出至MT檔案")
        wx.EVT_MENU(self, ID_EXPORT_MT, self.OnExportMT)

        tool_menu = wx.Menu()
        tool_menu.Append(ID_TOOL_COMBINE_COMMENT, u"結合留言至文章", u"結合留言至文章")
        wx.EVT_MENU(self, ID_TOOL_COMBINE_COMMENT, self.OnCombineComment)

        menuBar = wx.MenuBar()
        menuBar.Append(import_menu,u"匯入(&I)")
        menuBar.Append(export_menu,u"匯出(&E)")
        menuBar.Append(tool_menu,u"工具(&T)")

        self.SetMenuBar(menuBar)

    def __init__(self) :
        wx.Frame.__init__(self, None, wx.ID_ANY, u'BlogTrans 部落格搬家工具 ' + blogtrans.VERSION, size=(800,600))

        self.sizer = wx.BoxSizer(wx.HORIZONTAL)

        self.treectrl = BlogTreeCtrl(self)
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.treectrl)

        self.html = BlogHtmlWindow(self)

        self.sizer.Add(self.treectrl,1,wx.EXPAND)
        self.sizer.Add(self.html,2,wx.EXPAND)

        self.SetSizer(self.sizer)
        self.SetAutoLayout(1)

        self.__init_menubar()
        self.CreateStatusBar()

        self.Show(True)
        #self.setBlogData( self.genTestData() )

    # TODO: Bad design here, takes O(n^2) time complexity....
    def GetCheckedBlogData(self) :
        checked = self.treectrl.GetAllCheckedData()

        data = BlogData()
        comment_count = 0

        for article in self.blogdata.articles :
            if article in checked :
                a = copy.deepcopy(article)
                data.articles.append(a)
                a.comments = []

                for comment in article.comments :
                    if comment in checked :
                        a.comments.append(comment)
                        comment_count += 1

        return data

    def setBlogData(self, blogdata) :
        self.blogdata = blogdata
        self.treectrl.setBlogData(blogdata)

    def genTestData(self) :
        blogdata = BlogData()

        a = Article()
        a.author = "TestAuthor"
        a.title = "TestArticle"
        a.date = datetime.today()
        a.category = ["TestCategory"]
        a.status = Article.PUBLISH
        a.allow_comments = True
        a.allow_pings = True
        a.body = "TestArticleBody\n"
        a.extended_body = "TestArticleExtendedBody"
        a.excerpt = ""
        a.comments = []
        a.pings = []

        for i in range(0,2) :
            c = Comment()
            c.author = "Comment user %i " % i
            c.email = "user%i@gggggmail.com" % i
            c.url = "http://www.url%d.com" % i
            c.ip = "127.0.0.1"
            c.date = datetime.today()
            c.body = "Comment body %i" % i
            a.comments.append(c)

        blogdata.articles.append(a)

        return blogdata

    #Todo: lots of dumb code in callbacks, need refactoring
    def OnImportWretch(self, e) :
        dialog = wx.FileDialog(self)
        result = dialog.ShowModal()
        dialog.Destroy()
        if result != wx.ID_OK :
            return

        file = dialog.GetFilename()
        dir = dialog.GetDirectory()
        filename = os.path.join(dir, file)

        wi = WretchImporter(filename)
        blogdata = wi.parse()
        self.setBlogData(blogdata)

    def OnImportMT(self, e) :
        dialog = wx.FileDialog(self)
        result = dialog.ShowModal()
        dialog.Destroy()
        if result != wx.ID_OK :
            return
        file = dialog.GetFilename()
        dir = dialog.GetDirectory()
        filename = os.path.join(dir, file)

        mi = MTImporter(filename)
        blogdata = mi.parse()
        self.setBlogData(blogdata)

    def OnExportMT(self, e) :
        checked_data = self.GetCheckedBlogData()
        dialog = wx.FileDialog(self, style=wx.SAVE|wx.OVERWRITE_PROMPT)
        result = dialog.ShowModal()
        dialog.Destroy()
        if result != wx.ID_OK :
            return

        file = dialog.GetFilename()
        dir = dialog.GetDirectory()
        filename = os.path.join(dir, file)

        me = MTExporter(filename, checked_data)
        me.Export()

    def OnExportBlogger(self, e) :
        checked_data = self.GetCheckedBlogData()
        dialog = wx.FileDialog(self, style=wx.SAVE|wx.OVERWRITE_PROMPT)
        result = dialog.ShowModal()
        dialog.Destroy()
        if result != wx.ID_OK :
            return

        file = dialog.GetFilename()
        dir = dialog.GetDirectory()
        filename = os.path.join(dir, file)

        me = BloggerExporter(filename, checked_data)
        me.Export()

    def OnImportBlogger(self, e) :
        print "hi"
        dialog = wx.FileDialog(self)
        result = dialog.ShowModal()
        dialog.Destroy()
        if result != wx.ID_OK :
            return
        file = dialog.GetFilename()
        dir = dialog.GetDirectory()
        filename = os.path.join(dir, file)

        mi = BloggerImporter(filename)
        blogdata = mi.parse()
        self.setBlogData(blogdata)

    def OnCombineComment(self, e) :
        for a in self.blogdata.articles :
            if len(a.comments) :
                comment_htmls = map(CommentToHTML, a.comments)
                a.extended_body += "
" + "

".join(comment_htmls)
            a.comments = []
        self.setBlogData(self.blogdata)

    def OnSelChanged(self, e) :
        # Tofix:  seems a sync problem here
        data = e.GetItem().GetData()
        # print data.__class__
        if data.__class__ == Article :
            self.html.ShowArticle(data)
        elif data.__class__ == Comment :
            self.html.ShowComment(data)
        else :
            self.html.SetPage("")

<commit_msg>Clear category functionality<commit_after># coding=utf8

import copy, os
import wx._core as wx
import blogtrans
from wx.html import HtmlWindow

from BlogTreeCtrl import BlogTreeCtrl
from BlogHtmlCtrl import BlogHtmlWindow
from ProgressDialog import *

from blogtrans.data import *
from blogtrans.wretch.WretchImporter import WretchImporter
from blogtrans.mt import *

from blogtrans.blogger.BloggerExporter import *
from blogtrans.blogger.BloggerImporter import *

from blogtrans.ui.BlogHtmlCtrl import CommentToHTML

ID_IMPORT_WRETCH = wx.NewId()
ID_IMPORT_BLOGGER = wx.NewId()
ID_IMPORT_MT = wx.NewId()

ID_EXPORT_BLOGGER = wx.NewId()
ID_EXPORT_MT = wx.NewId()

ID_TOOL_COMBINE_COMMENT = wx.NewId()
ID_TOOL_CLEAR_CATEGORY = wx.NewId()

class MainWindow(wx.Frame):

    def __init_menubar(self) :

        #Todo: use a smarter way to manage menu...
        import_menu = wx.Menu()

        import_menu.Append(ID_IMPORT_WRETCH, u"無名XML檔案(&W)...",u"匯入無名XML檔案")
        wx.EVT_MENU(self, ID_IMPORT_WRETCH, self.OnImportWretch)

        # import_menu.Append(ID_IMPORT_BLOGGER, u"Blogger Atom XML(&B)...",u"匯入Blogger Atom XML")
        # wx.EVT_MENU(self, ID_IMPORT_BLOGGER, self.OnImportBlogger)

        import_menu.Append(ID_IMPORT_MT, u"&MT檔案...",u"匯入MT Import檔案")
        wx.EVT_MENU(self, ID_IMPORT_MT, self.OnImportMT)

        export_menu = wx.Menu()
        export_menu.Append(ID_EXPORT_BLOGGER, u"Blogger Atom XML(&B)...",u"匯出至Blogger Atom XML")
        wx.EVT_MENU(self, ID_EXPORT_BLOGGER, self.OnExportBlogger)

        export_menu.Append(ID_EXPORT_MT, u"&MT檔案...",u"匯出至MT檔案")
        wx.EVT_MENU(self, ID_EXPORT_MT, self.OnExportMT)

        tool_menu = wx.Menu()
        tool_menu.Append(ID_TOOL_COMBINE_COMMENT, u"結合留言至文章", u"結合留言至文章")
        wx.EVT_MENU(self, ID_TOOL_COMBINE_COMMENT, self.OnCombineComment)

        tool_menu.Append(ID_TOOL_CLEAR_CATEGORY, u"清除所有文章類別", u"清除所有文章類別")
        wx.EVT_MENU(self, ID_TOOL_CLEAR_CATEGORY, self.OnClearCategory)

        menuBar = wx.MenuBar()
        menuBar.Append(import_menu,u"匯入(&I)")
        menuBar.Append(export_menu,u"匯出(&E)")
        menuBar.Append(tool_menu,u"工具(&T)")

        self.SetMenuBar(menuBar)

    def __init__(self) :
        wx.Frame.__init__(self, None, wx.ID_ANY, u'BlogTrans 部落格搬家工具 ' + blogtrans.VERSION, size=(800,600))

        self.sizer = wx.BoxSizer(wx.HORIZONTAL)

        self.treectrl = BlogTreeCtrl(self)
        self.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, self.treectrl)

        self.html = BlogHtmlWindow(self)

        self.sizer.Add(self.treectrl,1,wx.EXPAND)
        self.sizer.Add(self.html,2,wx.EXPAND)

        self.SetSizer(self.sizer)
        self.SetAutoLayout(1)

        self.__init_menubar()
        self.CreateStatusBar()

        self.Show(True)
        #self.setBlogData( self.genTestData() )

    # TODO: Bad design here, takes O(n^2) time complexity....
    def GetCheckedBlogData(self) :
        checked = self.treectrl.GetAllCheckedData()

        data = BlogData()
        comment_count = 0

        for article in self.blogdata.articles :
            if article in checked :
                a = copy.deepcopy(article)
                data.articles.append(a)
                a.comments = []

                for comment in article.comments :
                    if comment in checked :
                        a.comments.append(comment)
                        comment_count += 1

        return data

    def setBlogData(self, blogdata) :
        self.blogdata = blogdata
        self.treectrl.setBlogData(blogdata)

    def genTestData(self) :
        blogdata = BlogData()

        a = Article()
        a.author = "TestAuthor"
        a.title = "TestArticle"
        a.date = datetime.today()
        a.category = ["TestCategory"]
        a.status = Article.PUBLISH
        a.allow_comments = True
        a.allow_pings = True
        a.body = "TestArticleBody\n"
        a.extended_body = "TestArticleExtendedBody"
        a.excerpt = ""
        a.comments = []
        a.pings = []

        for i in range(0,2) :
            c = Comment()
            c.author = "Comment user %i " % i
            c.email = "user%i@gggggmail.com" % i
            c.url = "http://www.url%d.com" % i
            c.ip = "127.0.0.1"
            c.date = datetime.today()
            c.body = "Comment body %i" % i
            a.comments.append(c)

        blogdata.articles.append(a)

        return blogdata

    #Todo: lots of dumb code in callbacks, need refactoring
    def OnImportWretch(self, e) :
        dialog = wx.FileDialog(self)
        result = dialog.ShowModal()
        dialog.Destroy()
        if result != wx.ID_OK :
            return

        file = dialog.GetFilename()
        dir = dialog.GetDirectory()
        filename = os.path.join(dir, file)

        wi = WretchImporter(filename)
        blogdata = wi.parse()
        self.setBlogData(blogdata)

    def OnImportMT(self, e) :
        dialog = wx.FileDialog(self)
        result = dialog.ShowModal()
        dialog.Destroy()
        if result != wx.ID_OK :
            return
        file = dialog.GetFilename()
        dir = dialog.GetDirectory()
        filename = os.path.join(dir, file)

        mi = MTImporter(filename)
        blogdata = mi.parse()
        self.setBlogData(blogdata)

    def OnExportMT(self, e) :
        checked_data = self.GetCheckedBlogData()
        dialog = wx.FileDialog(self, style=wx.SAVE|wx.OVERWRITE_PROMPT)
        result = dialog.ShowModal()
        dialog.Destroy()
        if result != wx.ID_OK :
            return

        file = dialog.GetFilename()
        dir = dialog.GetDirectory()
        filename = os.path.join(dir, file)

        me = MTExporter(filename, checked_data)
        me.Export()

    def OnExportBlogger(self, e) :
        checked_data = self.GetCheckedBlogData()
        dialog = wx.FileDialog(self, style=wx.SAVE|wx.OVERWRITE_PROMPT)
        result = dialog.ShowModal()
        dialog.Destroy()
        if result != wx.ID_OK :
            return

        file = dialog.GetFilename()
        dir = dialog.GetDirectory()
        filename = os.path.join(dir, file)

        me = BloggerExporter(filename, checked_data)
        me.Export()

    def OnImportBlogger(self, e) :
        print "hi"
        dialog = wx.FileDialog(self)
        result = dialog.ShowModal()
        dialog.Destroy()
        if result != wx.ID_OK :
            return
        file = dialog.GetFilename()
        dir = dialog.GetDirectory()
        filename = os.path.join(dir, file)

        mi = BloggerImporter(filename)
        blogdata = mi.parse()
        self.setBlogData(blogdata)

    def OnCombineComment(self, e) :
        for a in self.blogdata.articles :
            if len(a.comments) :
                comment_htmls = map(CommentToHTML, a.comments)
                a.extended_body += "
" + "

".join(comment_htmls)
            a.comments = []
        self.setBlogData(self.blogdata)

    def OnClearCategory(self, e) :
        for a in self.blogdata.articles :
            a.category = []
        self.setBlogData(self.blogdata)

    def OnSelChanged(self, e) :
        # Tofix:  seems a sync problem here
        data = e.GetItem().GetData()
        # print data.__class__
        if data.__class__ == Article :
            self.html.ShowArticle(data)
        elif data.__class__ == Comment :
            self.html.ShowComment(data)
        else :
            self.html.SetPage("")

<|endoftext|>


<commit_before>from django.contrib import admin
from django.urls import reverse
from django.utils.html import format_html
from django_summernote.widgets import SummernoteWidget

from bluebottle.activities.admin import ActivityChildAdmin, ContributionChildAdmin
from bluebottle.events.models import Event, Participant
from bluebottle.events.transitions import EventTransitions, ParticipantTransitions
from bluebottle.notifications.admin import MessageAdminInline
from bluebottle.utils.admin import export_as_csv_action
from bluebottle.utils.forms import FSMModelForm

class EventAdminForm(FSMModelForm):

    class Meta:
        model = Event
        fields = '__all__'
        widgets = {
            'description': SummernoteWidget(attrs={'height': 200})
        }

class ParticipantInline(admin.TabularInline):
    model = Participant

    raw_id_fields = ('user', )
    readonly_fields = ('created', 'status', 'participant')
    fields = ('participant', 'user', 'created', 'status', 'time_spent')

    extra = 0

    def participant(self, obj):
        url = reverse('admin:events_participant_change', args=(obj.id,))
        return format_html('{}', url, obj.id)

class ParticipantAdminForm(FSMModelForm):
    class Meta:
        model = Participant
        exclude = ['status', ]

@admin.register(Participant)
class ParticipantAdmin(ContributionChildAdmin):
    model = Participant
    form = ParticipantAdminForm
    list_display = ['user', 'status', 'time_spent', 'activity_link']
    raw_id_fields = ('user', 'activity')

    date_hierarchy = 'transition_date'

    export_to_csv_fields = (
        ('status', 'Status'),
        ('created', 'Created'),
        ('activity', 'Activity'),
        ('user__full_name', 'Owner'),
        ('user__email', 'Email'),
        ('time_spent', 'Time Spent'),
        ('contribution_date', 'Contribution Date'),
    )

    actions = [export_as_csv_action(fields=export_to_csv_fields)]

@admin.register(Event)
class EventAdmin(ActivityChildAdmin):
    form = EventAdminForm
    inlines = ActivityChildAdmin.inlines + (ParticipantInline, MessageAdminInline)
    list_display = [
        '__unicode__', 'initiative', 'status',
        'highlight', 'start', 'duration', 'created'
    ]
    search_fields = ['title', 'description']
    list_filter = ['status', 'is_online']
    date_hierarchy = 'start'

    base_model = Event

    readonly_fields = ActivityChildAdmin.readonly_fields + ['local_start', ]
    raw_id_fields = ActivityChildAdmin.raw_id_fields + ['location']

    detail_fields = (
        'description',
        'capacity',
        'start',
        'local_start',
        'duration',
        'registration_deadline',
        'is_online',
        'location',
        'location_hint',
    )

    export_to_csv_fields = (
        ('title', 'Title'),
        ('description', 'Description'),
        ('status', 'Status'),
        ('created', 'Created'),
        ('initiative__title', 'Initiative'),
        ('start', 'Start'),
        ('duration', 'Duration'),
        ('end', 'End'),
        ('registration_deadline', 'Registration Deadline'),
        ('owner__full_name', 'Owner'),
        ('owner__email', 'Email'),
        ('capacity', 'Capacity'),
        ('is_online', 'Will be hosted online?'),
        ('location', 'Location'),
        ('location_hint', 'Location Hint'),
        ('automatically_accept', 'Auto Accept Members'),
    )

    actions = [export_as_csv_action(fields=export_to_csv_fields)]

    def save_formset(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for instance in instances:
            # If we created a new participant through admin then
            # set it to succeeded when event is succeeded
            if (instance.__class__ == Participant and
                    not instance.pk and
                    form.instance.status == EventTransitions.values.succeeded):
                instance.time_spent = form.instance.duration
                instance.status = ParticipantTransitions.values.succeeded
            instance.save()
        super(EventAdmin, self).save_formset(request, form, formset, change)
<commit_msg>Update admin.py (#4105)<commit_after>from django.contrib import admin
from django.urls import reverse
from django.utils.html import format_html
from django_summernote.widgets import SummernoteWidget

from bluebottle.activities.admin import ActivityChildAdmin, ContributionChildAdmin
from bluebottle.events.models import Event, Participant
from bluebottle.events.transitions import EventTransitions, ParticipantTransitions
from bluebottle.notifications.admin import MessageAdminInline
from bluebottle.utils.admin import export_as_csv_action
from bluebottle.utils.forms import FSMModelForm

class EventAdminForm(FSMModelForm):

    class Meta:
        model = Event
        fields = '__all__'
        widgets = {
            'description': SummernoteWidget(attrs={'height': 200})
        }

class ParticipantInline(admin.TabularInline):
    model = Participant

    raw_id_fields = ('user', )
    readonly_fields = ('created', 'status', 'participant')
    fields = ('participant', 'user', 'created', 'status', 'time_spent')

    extra = 0

    def participant(self, obj):
        url = reverse('admin:events_participant_change', args=(obj.id,))
        return format_html('{}', url, obj.id)

class ParticipantAdminForm(FSMModelForm):
    class Meta:
        model = Participant
        exclude = ['status', ]

@admin.register(Participant)
class ParticipantAdmin(ContributionChildAdmin):
    model = Participant
    form = ParticipantAdminForm
    list_display = ['user', 'status', 'time_spent', 'activity_link']
    raw_id_fields = ('user', 'activity')

    date_hierarchy = 'transition_date'

    export_to_csv_fields = (
        ('status', 'Status'),
        ('created', 'Created'),
        ('activity', 'Activity'),
        ('user__full_name', 'Owner'),
        ('user__email', 'Email'),
        ('time_spent', 'Time Spent'),
        ('contribution_date', 'Contribution Date'),
    )

    actions = [export_as_csv_action(fields=export_to_csv_fields)]

@admin.register(Event)
class EventAdmin(ActivityChildAdmin):
    form = EventAdminForm
    inlines = ActivityChildAdmin.inlines + (ParticipantInline, MessageAdminInline)
    list_display = [
        '__unicode__', 'initiative', 'status',
        'highlight', 'start', 'duration', 'created'
    ]
    search_fields = ['title', 'description']
    list_filter = ['status', 'is_online']
    date_hierarchy = 'start'

    base_model = Event

    readonly_fields = ActivityChildAdmin.readonly_fields + ['local_start', ]
    raw_id_fields = ActivityChildAdmin.raw_id_fields + ['location']

    detail_fields = (
        'description',
        'capacity',
        'start',
        'local_start',
        'duration',
        # 'registration_deadline',  # Disabled until feature is ready - BB-16853
        'is_online',
        'location',
        'location_hint',
    )

    export_to_csv_fields = (
        ('title', 'Title'),
        ('description', 'Description'),
        ('status', 'Status'),
        ('created', 'Created'),
        ('initiative__title', 'Initiative'),
        ('start', 'Start'),
        ('duration', 'Duration'),
        ('end', 'End'),
        ('registration_deadline', 'Registration Deadline'),
        ('owner__full_name', 'Owner'),
        ('owner__email', 'Email'),
        ('capacity', 'Capacity'),
        ('is_online', 'Will be hosted online?'),
        ('location', 'Location'),
        ('location_hint', 'Location Hint'),
        ('automatically_accept', 'Auto Accept Members'),
    )

    actions = [export_as_csv_action(fields=export_to_csv_fields)]

    def save_formset(self, request, form, formset, change):
        instances = formset.save(commit=False)
        for instance in instances:
            # If we created a new participant through admin then
            # set it to succeeded when event is succeeded
            if (instance.__class__ == Participant and
                    not instance.pk and
                    form.instance.status == EventTransitions.values.succeeded):
                instance.time_spent = form.instance.duration
                instance.status = ParticipantTransitions.values.succeeded
            instance.save()
        super(EventAdmin, self).save_formset(request, form, formset, change)
<|endoftext|>


<commit_before>import mimetypes
from babel.numbers import get_currency_name

from rest_framework import serializers

from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.db import models
from django.conf import settings
from django.utils.functional import lazy
from django.utils.translation import ugettext as _

import sorl.thumbnail
from djmoney.models.fields import MoneyField as DjangoMoneyField

import xml.etree.cElementTree as et

from bluebottle.clients import properties

from .utils import clean_html

def get_currency_choices():
    currencies = []
    for method in properties.PAYMENT_METHODS:
        currencies += method['currencies'].keys()

    return [(currency, get_currency_name(currency, locale='en')) for currency in set(currencies)]

def get_default_currency():
    return getattr(properties, 'DEFAULT_CURRENCY')

class MoneyField(DjangoMoneyField):
    def __init__(self, verbose_name=None, name=None,
                 max_digits=12, decimal_places=2, default=None,
                 default_currency=lazy(get_default_currency, str)(),
                 currency_choices=lazy(get_currency_choices, tuple)(),
                 **kwargs):
        super(MoneyField, self).__init__(
            verbose_name=verbose_name, name=name,
            max_digits=max_digits, decimal_places=decimal_places, default=default,
            default_currency=default_currency,
            currency_choices=currency_choices,
            **kwargs)

# Validation references:
# http://www.mobilefish.com/services/elfproef/elfproef.php
# http://www.credit-card.be/BankAccount/ValidationRules.htm#NL_Validation
class DutchBankAccountFieldValidator(RegexValidator):
    main_message = _("Dutch bank account numbers have 1 - 7, 9 or 10 digits.")

    def __init__(self, regex=None, message=None, code=None):
        super(DutchBankAccountFieldValidator, self).__init__(regex='^[0-9]+$',
                                                             message=self.main_message)

    def __call__(self, value):
        super(DutchBankAccountFieldValidator, self).__call__(value)
        if len(value) != 9 and len(value) != 10 and not 1 <= len(value) <= 7:
            raise ValidationError(self.main_message)

        # Perform the eleven test validation on non-PostBank numbers.
        if len(value) == 9 or len(value) == 10:
            if len(value) == 9:
                value = "0" + value

            eleven_test_sum = sum(
                [int(a) * b for a, b in zip(value, range(1, 11))])
            if eleven_test_sum % 11 != 0:
                raise ValidationError(_("Invalid Dutch bank account number."))

class DutchBankAccountField(models.CharField):
    def __init__(self, *args, **kwargs):
        kwargs.setdefault('max_length', 10)
        super(DutchBankAccountField, self).__init__(*args, **kwargs)
        self.validators.append(DutchBankAccountFieldValidator())

class ImageField(sorl.thumbnail.fields.ImageField):
    """ Image field that only allow certain mime-types.

    Overriden from sorl.thumbnail.fields.ImageField.

    The list of valid mime-types can be set using the IMAGE_ALLOWED_MIME_TYPES setting.
    """

    def formfield(self, **kwargs):
        defaults = {'form_class': RestrictedImageFormField}
        defaults.update(kwargs)
        return super(ImageField, self).formfield(**defaults)

class RestrictedImageFormField(sorl.thumbnail.fields.ImageFormField):
    """ Actual FormField that does the validation of the mime-types."""

    def to_python(self, data):
        """
        Checks that the file-upload field data contains a valid image (GIF,
        JPG, PNG, possibly others -- whatever the engine supports).

        If the item cannot be converted to an image, check if the file is and svg
        """
        if data and data.content_type not in settings.IMAGE_ALLOWED_MIME_TYPES:
            raise forms.ValidationError(self.error_messages['invalid_image'])

        if (
            hasattr(data.name) and
            mimetypes.guess_type(data.name)[0] not in settings.IMAGE_ALLOWED_MIME_TYPES
        ):
            raise forms.ValidationError(self.error_messages['invalid_image'])

        try:
            return super(RestrictedImageFormField, self).to_python(data)
        except ValidationError:
            test_file = super(sorl.thumbnail.fields.ImageFormField, self).to_python(data)

            if self.is_svg(test_file):
                return test_file
            else:
                raise

    def is_svg(self, f):
        """
        Check if provided file is svg
        """
        f.seek(0)
        tag = None
        try:
            for event, el in et.iterparse(f, ('start',)):
                tag = el.tag
                break
        except et.ParseError:
            pass
        return tag == '{http://www.w3.org/2000/svg}svg'

# If south is installed, ensure that DutchBankAccountField will be introspected just like a normal CharField
try:
    from south.modelsinspector import add_introspection_rules

    add_introspection_rules([], ["^apps.fund\.fields\.DutchBankAccountField"])
except ImportError:
    pass

class SafeField(serializers.CharField):
    def to_representation(self, value):
        """ Reading / Loading the story field """
        return clean_html(value)

    def to_internal_value(self, data):
        """
        Saving the story text

        Convert > and < back to HTML tags so Beautiful Soup can clean
        unwanted tags. Script tags are sent by redactor as
        "<;script>;", Iframe tags have just one semicolon.
        """
        data = data.replace("<;", "<").replace(">;", ">")
        data = data.replace("<", "<").replace(">", ">")
        return unicode(clean_html(data))

class PrivateFileField(models.FileField):
    def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
        # Check if upload_to already has private path
        # This fixes loops and randomly added migrations
        if not upload_to.startswith('private'):
            upload_to = 'private/{}'.format(upload_to)
        super(PrivateFileField, self).__init__(
            verbose_name=verbose_name, name=name, upload_to=upload_to, storage=storage, **kwargs
        )
<commit_msg>Correctly call hasattr<commit_after>import mimetypes
from babel.numbers import get_currency_name

from rest_framework import serializers

from django import forms
from django.core.exceptions import ValidationError
from django.core.validators import RegexValidator
from django.db import models
from django.conf import settings
from django.utils.functional import lazy
from django.utils.translation import ugettext as _

import sorl.thumbnail
from djmoney.models.fields import MoneyField as DjangoMoneyField

import xml.etree.cElementTree as et

from bluebottle.clients import properties

from .utils import clean_html

def get_currency_choices():
    currencies = []
    for method in properties.PAYMENT_METHODS:
        currencies += method['currencies'].keys()

    return [(currency, get_currency_name(currency, locale='en')) for currency in set(currencies)]

def get_default_currency():
    return getattr(properties, 'DEFAULT_CURRENCY')

class MoneyField(DjangoMoneyField):
    def __init__(self, verbose_name=None, name=None,
                 max_digits=12, decimal_places=2, default=None,
                 default_currency=lazy(get_default_currency, str)(),
                 currency_choices=lazy(get_currency_choices, tuple)(),
                 **kwargs):
        super(MoneyField, self).__init__(
            verbose_name=verbose_name, name=name,
            max_digits=max_digits, decimal_places=decimal_places, default=default,
            default_currency=default_currency,
            currency_choices=currency_choices,
            **kwargs)

# Validation references:
# http://www.mobilefish.com/services/elfproef/elfproef.php
# http://www.credit-card.be/BankAccount/ValidationRules.htm#NL_Validation
class DutchBankAccountFieldValidator(RegexValidator):
    main_message = _("Dutch bank account numbers have 1 - 7, 9 or 10 digits.")

    def __init__(self, regex=None, message=None, code=None):
        super(DutchBankAccountFieldValidator, self).__init__(regex='^[0-9]+$',
                                                             message=self.main_message)

    def __call__(self, value):
        super(DutchBankAccountFieldValidator, self).__call__(value)
        if len(value) != 9 and len(value) != 10 and not 1 <= len(value) <= 7:
            raise ValidationError(self.main_message)

        # Perform the eleven test validation on non-PostBank numbers.
        if len(value) == 9 or len(value) == 10:
            if len(value) == 9:
                value = "0" + value

            eleven_test_sum = sum(
                [int(a) * b for a, b in zip(value, range(1, 11))])
            if eleven_test_sum % 11 != 0:
                raise ValidationError(_("Invalid Dutch bank account number."))

class DutchBankAccountField(models.CharField):
    def __init__(self, *args, **kwargs):
        kwargs.setdefault('max_length', 10)
        super(DutchBankAccountField, self).__init__(*args, **kwargs)
        self.validators.append(DutchBankAccountFieldValidator())

class ImageField(sorl.thumbnail.fields.ImageField):
    """ Image field that only allow certain mime-types.

    Overriden from sorl.thumbnail.fields.ImageField.

    The list of valid mime-types can be set using the IMAGE_ALLOWED_MIME_TYPES setting.
    """

    def formfield(self, **kwargs):
        defaults = {'form_class': RestrictedImageFormField}
        defaults.update(kwargs)
        return super(ImageField, self).formfield(**defaults)

class RestrictedImageFormField(sorl.thumbnail.fields.ImageFormField):
    """ Actual FormField that does the validation of the mime-types."""

    def to_python(self, data):
        """
        Checks that the file-upload field data contains a valid image (GIF,
        JPG, PNG, possibly others -- whatever the engine supports).

        If the item cannot be converted to an image, check if the file is and svg
        """
        if data and data.content_type not in settings.IMAGE_ALLOWED_MIME_TYPES:
            raise forms.ValidationError(self.error_messages['invalid_image'])

        if (
            hasattr(data, 'name') and
            mimetypes.guess_type(data.name)[0] not in settings.IMAGE_ALLOWED_MIME_TYPES
        ):
            raise forms.ValidationError(self.error_messages['invalid_image'])

        try:
            return super(RestrictedImageFormField, self).to_python(data)
        except ValidationError:
            test_file = super(sorl.thumbnail.fields.ImageFormField, self).to_python(data)

            if self.is_svg(test_file):
                return test_file
            else:
                raise

    def is_svg(self, f):
        """
        Check if provided file is svg
        """
        f.seek(0)
        tag = None
        try:
            for event, el in et.iterparse(f, ('start',)):
                tag = el.tag
                break
        except et.ParseError:
            pass
        return tag == '{http://www.w3.org/2000/svg}svg'

# If south is installed, ensure that DutchBankAccountField will be introspected just like a normal CharField
try:
    from south.modelsinspector import add_introspection_rules

    add_introspection_rules([], ["^apps.fund\.fields\.DutchBankAccountField"])
except ImportError:
    pass

class SafeField(serializers.CharField):
    def to_representation(self, value):
        """ Reading / Loading the story field """
        return clean_html(value)

    def to_internal_value(self, data):
        """
        Saving the story text

        Convert > and < back to HTML tags so Beautiful Soup can clean
        unwanted tags. Script tags are sent by redactor as
        "<;script>;", Iframe tags have just one semicolon.
        """
        data = data.replace("<;", "<").replace(">;", ">")
        data = data.replace("<", "<").replace(">", ">")
        return unicode(clean_html(data))

class PrivateFileField(models.FileField):
    def __init__(self, verbose_name=None, name=None, upload_to='', storage=None, **kwargs):
        # Check if upload_to already has private path
        # This fixes loops and randomly added migrations
        if not upload_to.startswith('private'):
            upload_to = 'private/{}'.format(upload_to)
        super(PrivateFileField, self).__init__(
            verbose_name=verbose_name, name=name, upload_to=upload_to, storage=storage, **kwargs
        )
<|endoftext|>