<commit_before>/**
 * Copyright (c) 2016-2018 mvs developers
 *
 * This file is part of metaverse-explorer.
 *
 * metaverse-explorer is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <functional>
#include <memory>
#include <string>
#include <array>

#include <metaverse/explorer/command.hpp>
#include <metaverse/explorer/dispatch.hpp>
#include <metaverse/explorer/extensions/command_extension.hpp>
#include <metaverse/explorer/extensions/command_extension_func.hpp>
#include <metaverse/explorer/extensions/commands/shutdown.hpp>
#include <metaverse/explorer/extensions/commands/stopmining.hpp>
#include <metaverse/explorer/extensions/commands/startmining.hpp>
#include <metaverse/explorer/extensions/commands/getinfo.hpp>
#include <metaverse/explorer/extensions/commands/getheight.hpp>
#include <metaverse/explorer/extensions/commands/getpeerinfo.hpp>
#include <metaverse/explorer/extensions/commands/getaddressetp.hpp>
#include <metaverse/explorer/extensions/commands/addnode.hpp>
#include <metaverse/explorer/extensions/commands/getmininginfo.hpp>
#include <metaverse/explorer/extensions/commands/getblockheader.hpp>
#include <metaverse/explorer/extensions/commands/fetchheaderext.hpp>
#include <metaverse/explorer/extensions/commands/gettx.hpp>
#include <metaverse/explorer/extensions/commands/dumpkeyfile.hpp>
#include <metaverse/explorer/extensions/commands/importkeyfile.hpp>
#include <metaverse/explorer/extensions/commands/importaccount.hpp>
#include <metaverse/explorer/extensions/commands/getnewaccount.hpp>
#include <metaverse/explorer/extensions/commands/getaccount.hpp>
#include <metaverse/explorer/extensions/commands/deleteaccount.hpp>
#include <metaverse/explorer/extensions/commands/listaddresses.hpp>
#include <metaverse/explorer/extensions/commands/getnewaddress.hpp>
#include <metaverse/explorer/extensions/commands/getblock.hpp>
#include <metaverse/explorer/extensions/commands/validateaddress.hpp>
#include <metaverse/explorer/extensions/commands/listbalances.hpp>
#include <metaverse/explorer/extensions/commands/getbalance.hpp>
#include <metaverse/explorer/extensions/commands/listtxs.hpp>
#include <metaverse/explorer/extensions/commands/deposit.hpp>
#include <metaverse/explorer/extensions/commands/listassets.hpp>
#include <metaverse/explorer/extensions/commands/getasset.hpp>
#include <metaverse/explorer/extensions/commands/secondaryissue.hpp>
#include <metaverse/explorer/extensions/commands/getaddressasset.hpp>
#include <metaverse/explorer/extensions/commands/getaccountasset.hpp>
#include <metaverse/explorer/extensions/commands/getassetview.hpp>
#include <metaverse/explorer/extensions/commands/createasset.hpp>
#include <metaverse/explorer/extensions/commands/registermit.hpp>
#include <metaverse/explorer/extensions/commands/transfermit.hpp>
#include <metaverse/explorer/extensions/commands/listmits.hpp>
#include <metaverse/explorer/extensions/commands/getmit.hpp>
#include <metaverse/explorer/extensions/commands/registerdid.hpp>
#include <metaverse/explorer/extensions/commands/send.hpp>
#include <metaverse/explorer/extensions/commands/sendfrom.hpp>
#include <metaverse/explorer/extensions/commands/sendmore.hpp>
#include <metaverse/explorer/extensions/commands/sendasset.hpp>
#include <metaverse/explorer/extensions/commands/sendassetfrom.hpp>
#include <metaverse/explorer/extensions/commands/swaptoken.hpp>
#include <metaverse/explorer/extensions/commands/listdids.hpp>
#include <metaverse/explorer/extensions/commands/deletelocalasset.hpp>
#include <metaverse/explorer/extensions/commands/issue.hpp>
#include <metaverse/explorer/extensions/commands/burn.hpp>
#include <metaverse/explorer/extensions/commands/transfercert.hpp>
#include <metaverse/explorer/extensions/commands/issuecert.hpp>
#include <metaverse/explorer/extensions/commands/getwork.hpp>
#include <metaverse/explorer/extensions/commands/submitwork.hpp>
#include <metaverse/explorer/extensions/commands/setminingaccount.hpp>
#include <metaverse/explorer/extensions/commands/changepasswd.hpp>
#include <metaverse/explorer/extensions/commands/getmemorypool.hpp>
#include <metaverse/explorer/extensions/commands/createmultisigtx.hpp>
#include <metaverse/explorer/extensions/commands/createrawtx.hpp>
#include <metaverse/explorer/extensions/commands/decoderawtx.hpp>
#include <metaverse/explorer/extensions/commands/deletemultisig.hpp>
#include <metaverse/explorer/extensions/commands/getnewmultisig.hpp>
#include <metaverse/explorer/extensions/commands/getpublickey.hpp>
#include <metaverse/explorer/extensions/commands/listmultisig.hpp>
#include <metaverse/explorer/extensions/commands/sendrawtx.hpp>
#include <metaverse/explorer/extensions/commands/signmultisigtx.hpp>
#include <metaverse/explorer/extensions/commands/signrawtx.hpp>
#include <metaverse/explorer/extensions/commands/didchangeaddress.hpp>
#include <metaverse/explorer/extensions/commands/getdid.hpp>

namespace libbitcoin {
namespace explorer {

void broadcast_extension(const function<void(shared_ptr<command>)> func, std::ostream& os)
{
    using namespace std;
    using namespace commands;

    os <<"\r\n";
    // account
    func(make_shared<getnewaccount>());
    func(make_shared<getaccount>());
    func(make_shared<deleteaccount>());
    func(make_shared<importaccount>());
    func(make_shared<changepasswd>());
    func(make_shared<getnewaddress>());
    func(make_shared<validateaddress>());
    func(make_shared<listaddresses>());
    func(make_shared<dumpkeyfile>());
    func(make_shared<importkeyfile>());

    os <<"\r\n";
    // system
    func(make_shared<shutdown>());
    func(make_shared<getinfo>());
    func(make_shared<addnode>());
    func(make_shared<getpeerinfo>());

    // miming
    func(make_shared<startmining>());
    func(make_shared<stopmining>());
    func(make_shared<getmininginfo>());
    func(make_shared<setminingaccount>());
    func(make_shared<getwork>());
    func(make_shared<submitwork>());
    func(make_shared<getmemorypool>());

    os <<"\r\n";
    // block & tx
    func(make_shared<getheight>());
    func(make_shared<getblock>());
    func(make_shared<getblockheader>());
    func(make_shared<fetchheaderext>());
    func(make_shared<gettx>());
    func(make_shared<listtxs>());

    // raw tx
    func(make_shared<createrawtx>());
    func(make_shared<decoderawtx>());
    func(make_shared<signrawtx>());
    func(make_shared<sendrawtx>());

    os <<"\r\n";
    // multi-sig
    func(make_shared<getpublickey>());
    func(make_shared<createmultisigtx>());
    func(make_shared<getnewmultisig>());
    func(make_shared<listmultisig>());
    func(make_shared<deletemultisig>());
    func(make_shared<signmultisigtx>());

    os <<"\r\n";
    // etp
    func(make_shared<send>());
    func(make_shared<sendmore>());
    func(make_shared<sendfrom>());
    func(make_shared<deposit>());
    func(make_shared<listbalances>());
    func(make_shared<getbalance>());
    func(make_shared<getaddressetp>());

    os <<"\r\n";
    // asset
    func(make_shared<createasset>());
    func(make_shared<deletelocalasset>());
    func(make_shared<issue>());
    func(make_shared<secondaryissue>());
    func(make_shared<sendasset>());
    func(make_shared<sendassetfrom>());
    func(make_shared<listassets>());
    func(make_shared<getasset>());
    func(make_shared<getaccountasset>());
    func(make_shared<getassetview>());
    func(make_shared<getaddressasset>());
    func(make_shared<burn>());
    func(make_shared<swaptoken>());

    os <<"\r\n";
    // cert
    func(make_shared<issuecert>());
    func(make_shared<transfercert>());

    os <<"\r\n";
    // mit
    func(make_shared<registermit>());
    func(make_shared<transfermit>());
    func(make_shared<listmits>());
    func(make_shared<getmit>());

    os <<"\r\n";
    //did
    func(make_shared<registerdid>());
    func(make_shared<didchangeaddress>());
    func(make_shared<listdids>());
    func(make_shared<getdid>());
}

shared_ptr<command> find_extension(const string& symbol)
{
    using namespace std;
    using namespace commands;

    // account
    if (symbol == getnewaccount::symbol())
        return make_shared<getnewaccount>();
    if (symbol == getaccount::symbol())
        return make_shared<getaccount>();
    if (symbol == deleteaccount::symbol())
        return make_shared<deleteaccount>();
    if (symbol == changepasswd::symbol())
        return make_shared<changepasswd>();
    if (symbol == validateaddress::symbol())
        return make_shared<validateaddress>();
    if (symbol == getnewaddress::symbol())
        return make_shared<getnewaddress>();
    if (symbol == listaddresses::symbol())
        return make_shared<listaddresses>();
    if (symbol == importaccount::symbol())
        return make_shared<importaccount>();
    if (symbol == dumpkeyfile::symbol() || symbol == "exportaccountasfile")
        return make_shared<dumpkeyfile>();
    if (symbol == importkeyfile::symbol() || symbol == "importaccountfromfile")
        return make_shared<importkeyfile>();

    // system
    if (symbol == shutdown::symbol())
        return make_shared<shutdown>();
    if (symbol == getinfo::symbol())
        return make_shared<getinfo>();
    if (symbol == addnode::symbol())
        return make_shared<addnode>();
    if (symbol == getpeerinfo::symbol())
        return make_shared<getpeerinfo>();

    // mining
    if (symbol == stopmining::symbol() || symbol == "stop")
        return make_shared<stopmining>();
    if (symbol == startmining::symbol() || symbol == "start")
        return make_shared<startmining>();
    if (symbol == setminingaccount::symbol())
        return make_shared<setminingaccount>();
    if (symbol == getmininginfo::symbol())
        return make_shared<getmininginfo>();
    if ((symbol == getwork::symbol()) || (symbol == "eth_getWork"))
        return make_shared<getwork>();
    if ((symbol == submitwork::symbol()) || ( symbol == "eth_submitWork"))
        return make_shared<submitwork>();
    if (symbol == getmemorypool::symbol())
        return make_shared<getmemorypool>();

    // block & tx
    if (symbol == getheight::symbol())
        return make_shared<getheight>();
    if (symbol == "fetch-height")
        return make_shared<getheight>(symbol);
    if (symbol == getblock::symbol())
        return make_shared<getblock>();
    if (symbol == "getbestblockhash")
        return make_shared<getblockheader>(symbol);
    if (symbol == getblockheader::symbol() || symbol == "fetch-header" || symbol == "getbestblockheader")
        return make_shared<getblockheader>();
    if (symbol == fetchheaderext::symbol())
        return make_shared<fetchheaderext>();
    if (symbol == gettx::symbol() || symbol == "gettransaction")
        return make_shared<gettx>();
    if (symbol == "fetch-tx")
        return make_shared<gettx>(symbol);
    if (symbol == listtxs::symbol())
        return make_shared<listtxs>();

    // raw tx
    if (symbol == createrawtx::symbol())
        return make_shared<createrawtx>();
    if (symbol == decoderawtx::symbol())
        return make_shared<decoderawtx>();
    if (symbol == signrawtx::symbol())
        return make_shared<signrawtx>();
    if (symbol == sendrawtx::symbol())
        return make_shared<sendrawtx>();

    // multi-sig
    if (symbol == getpublickey::symbol())
        return make_shared<getpublickey>();
    if (symbol == getnewmultisig::symbol())
        return make_shared<getnewmultisig>();
    if (symbol == listmultisig::symbol())
        return make_shared<listmultisig>();
    if (symbol == deletemultisig::symbol())
        return make_shared<deletemultisig>();
    if (symbol == createmultisigtx::symbol())
        return make_shared<createmultisigtx>();
    if (symbol == signmultisigtx::symbol())
        return make_shared<signmultisigtx>();

    // etp
    if (symbol == listbalances::symbol())
        return make_shared<listbalances>();
    if (symbol == getbalance::symbol())
        return make_shared<getbalance>();
    if (symbol == getaddressetp::symbol() || symbol == "fetch-balance")
        return make_shared<getaddressetp>();
    if (symbol == deposit::symbol())
        return make_shared<deposit>();
    if (symbol == send::symbol() || symbol == "didsend")
        return make_shared<send>();
    if (symbol == sendmore::symbol() || symbol == "didsendmore")
        return make_shared<sendmore>();
    if (symbol == sendfrom::symbol() || symbol == "didsendfrom")
        return make_shared<sendfrom>();

    // asset
    if (symbol == createasset::symbol())
        return make_shared<createasset>();
    if (symbol == deletelocalasset::symbol() || symbol == "deleteasset" )
        return make_shared<deletelocalasset>();
    if (symbol == listassets::symbol())
        return make_shared<listassets>();
    if (symbol == getasset::symbol())
        return make_shared<getasset>();
    if (symbol == getaccountasset::symbol())
        return make_shared<getaccountasset>();
    if (symbol == getassetview::symbol())
        return make_shared<getassetview>();
    if (symbol == getaddressasset::symbol())
        return make_shared<getaddressasset>();
    if (symbol == issue::symbol())
        return make_shared<issue>();
    if (symbol == secondaryissue::symbol() || (symbol == "additionalissue") )
        return make_shared<secondaryissue>();
    if (symbol == sendasset::symbol() || symbol == "didsendasset")
        return make_shared<sendasset>();
    if (symbol == sendassetfrom::symbol() || symbol == "didsendassetfrom")
        return make_shared<sendassetfrom>();
    if (symbol == burn::symbol())
        return make_shared<burn>();
    if (symbol == swaptoken::symbol())
        return make_shared<swaptoken>();

    // cert
    if (symbol == transfercert::symbol())
        return make_shared<transfercert>();
    if (symbol == issuecert::symbol())
        return make_shared<issuecert>();

    // mit
    if (symbol == registermit::symbol())
        return make_shared<registermit>();
    if (symbol == transfermit::symbol())
        return make_shared<transfermit>();
    if (symbol == listmits::symbol())
        return make_shared<listmits>();
    if (symbol == getmit::symbol())
        return make_shared<getmit>();

    // did
    if (symbol == registerdid::symbol())
        return make_shared<registerdid>();
    if (symbol == didchangeaddress::symbol())
        return make_shared<didchangeaddress>();
    if (symbol == listdids::symbol())
        return make_shared<listdids>();
    if (symbol == getdid::symbol())
        return make_shared<getdid>();

    return nullptr;
}

std::string formerly_extension(const string& former)
{
    return "";
}

} // namespace explorer
} // namespace libbitcoin

<commit_msg>disable getassetview<commit_after>/**
 * Copyright (c) 2016-2018 mvs developers
 *
 * This file is part of metaverse-explorer.
 *
 * metaverse-explorer is free software: you can redistribute it and/or
 * modify it under the terms of the GNU Affero General Public License with
 * additional permissions to the one published by the Free Software
 * Foundation, either version 3 of the License, or (at your option)
 * any later version. For more information see LICENSE.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
 */

#include <functional>
#include <memory>
#include <string>
#include <array>

#include <metaverse/explorer/command.hpp>
#include <metaverse/explorer/dispatch.hpp>
#include <metaverse/explorer/extensions/command_extension.hpp>
#include <metaverse/explorer/extensions/command_extension_func.hpp>
#include <metaverse/explorer/extensions/commands/shutdown.hpp>
#include <metaverse/explorer/extensions/commands/stopmining.hpp>
#include <metaverse/explorer/extensions/commands/startmining.hpp>
#include <metaverse/explorer/extensions/commands/getinfo.hpp>
#include <metaverse/explorer/extensions/commands/getheight.hpp>
#include <metaverse/explorer/extensions/commands/getpeerinfo.hpp>
#include <metaverse/explorer/extensions/commands/getaddressetp.hpp>
#include <metaverse/explorer/extensions/commands/addnode.hpp>
#include <metaverse/explorer/extensions/commands/getmininginfo.hpp>
#include <metaverse/explorer/extensions/commands/getblockheader.hpp>
#include <metaverse/explorer/extensions/commands/fetchheaderext.hpp>
#include <metaverse/explorer/extensions/commands/gettx.hpp>
#include <metaverse/explorer/extensions/commands/dumpkeyfile.hpp>
#include <metaverse/explorer/extensions/commands/importkeyfile.hpp>
#include <metaverse/explorer/extensions/commands/importaccount.hpp>
#include <metaverse/explorer/extensions/commands/getnewaccount.hpp>
#include <metaverse/explorer/extensions/commands/getaccount.hpp>
#include <metaverse/explorer/extensions/commands/deleteaccount.hpp>
#include <metaverse/explorer/extensions/commands/listaddresses.hpp>
#include <metaverse/explorer/extensions/commands/getnewaddress.hpp>
#include <metaverse/explorer/extensions/commands/getblock.hpp>
#include <metaverse/explorer/extensions/commands/validateaddress.hpp>
#include <metaverse/explorer/extensions/commands/listbalances.hpp>
#include <metaverse/explorer/extensions/commands/getbalance.hpp>
#include <metaverse/explorer/extensions/commands/listtxs.hpp>
#include <metaverse/explorer/extensions/commands/deposit.hpp>
#include <metaverse/explorer/extensions/commands/listassets.hpp>
#include <metaverse/explorer/extensions/commands/getasset.hpp>
#include <metaverse/explorer/extensions/commands/secondaryissue.hpp>
#include <metaverse/explorer/extensions/commands/getaddressasset.hpp>
#include <metaverse/explorer/extensions/commands/getaccountasset.hpp>
#include <metaverse/explorer/extensions/commands/getassetview.hpp>
#include <metaverse/explorer/extensions/commands/createasset.hpp>
#include <metaverse/explorer/extensions/commands/registermit.hpp>
#include <metaverse/explorer/extensions/commands/transfermit.hpp>
#include <metaverse/explorer/extensions/commands/listmits.hpp>
#include <metaverse/explorer/extensions/commands/getmit.hpp>
#include <metaverse/explorer/extensions/commands/registerdid.hpp>
#include <metaverse/explorer/extensions/commands/send.hpp>
#include <metaverse/explorer/extensions/commands/sendfrom.hpp>
#include <metaverse/explorer/extensions/commands/sendmore.hpp>
#include <metaverse/explorer/extensions/commands/sendasset.hpp>
#include <metaverse/explorer/extensions/commands/sendassetfrom.hpp>
#include <metaverse/explorer/extensions/commands/swaptoken.hpp>
#include <metaverse/explorer/extensions/commands/listdids.hpp>
#include <metaverse/explorer/extensions/commands/deletelocalasset.hpp>
#include <metaverse/explorer/extensions/commands/issue.hpp>
#include <metaverse/explorer/extensions/commands/burn.hpp>
#include <metaverse/explorer/extensions/commands/transfercert.hpp>
#include <metaverse/explorer/extensions/commands/issuecert.hpp>
#include <metaverse/explorer/extensions/commands/getwork.hpp>
#include <metaverse/explorer/extensions/commands/submitwork.hpp>
#include <metaverse/explorer/extensions/commands/setminingaccount.hpp>
#include <metaverse/explorer/extensions/commands/changepasswd.hpp>
#include <metaverse/explorer/extensions/commands/getmemorypool.hpp>
#include <metaverse/explorer/extensions/commands/createmultisigtx.hpp>
#include <metaverse/explorer/extensions/commands/createrawtx.hpp>
#include <metaverse/explorer/extensions/commands/decoderawtx.hpp>
#include <metaverse/explorer/extensions/commands/deletemultisig.hpp>
#include <metaverse/explorer/extensions/commands/getnewmultisig.hpp>
#include <metaverse/explorer/extensions/commands/getpublickey.hpp>
#include <metaverse/explorer/extensions/commands/listmultisig.hpp>
#include <metaverse/explorer/extensions/commands/sendrawtx.hpp>
#include <metaverse/explorer/extensions/commands/signmultisigtx.hpp>
#include <metaverse/explorer/extensions/commands/signrawtx.hpp>
#include <metaverse/explorer/extensions/commands/didchangeaddress.hpp>
#include <metaverse/explorer/extensions/commands/getdid.hpp>

namespace libbitcoin {
namespace explorer {

void broadcast_extension(const function<void(shared_ptr<command>)> func, std::ostream& os)
{
    using namespace std;
    using namespace commands;

    os <<"\r\n";
    // account
    func(make_shared<getnewaccount>());
    func(make_shared<getaccount>());
    func(make_shared<deleteaccount>());
    func(make_shared<importaccount>());
    func(make_shared<changepasswd>());
    func(make_shared<getnewaddress>());
    func(make_shared<validateaddress>());
    func(make_shared<listaddresses>());
    func(make_shared<dumpkeyfile>());
    func(make_shared<importkeyfile>());

    os <<"\r\n";
    // system
    func(make_shared<shutdown>());
    func(make_shared<getinfo>());
    func(make_shared<addnode>());
    func(make_shared<getpeerinfo>());

    // miming
    func(make_shared<startmining>());
    func(make_shared<stopmining>());
    func(make_shared<getmininginfo>());
    func(make_shared<setminingaccount>());
    func(make_shared<getwork>());
    func(make_shared<submitwork>());
    func(make_shared<getmemorypool>());

    os <<"\r\n";
    // block & tx
    func(make_shared<getheight>());
    func(make_shared<getblock>());
    func(make_shared<getblockheader>());
    func(make_shared<fetchheaderext>());
    func(make_shared<gettx>());
    func(make_shared<listtxs>());

    // raw tx
    func(make_shared<createrawtx>());
    func(make_shared<decoderawtx>());
    func(make_shared<signrawtx>());
    func(make_shared<sendrawtx>());

    os <<"\r\n";
    // multi-sig
    func(make_shared<getpublickey>());
    func(make_shared<createmultisigtx>());
    func(make_shared<getnewmultisig>());
    func(make_shared<listmultisig>());
    func(make_shared<deletemultisig>());
    func(make_shared<signmultisigtx>());

    os <<"\r\n";
    // etp
    func(make_shared<send>());
    func(make_shared<sendmore>());
    func(make_shared<sendfrom>());
    func(make_shared<deposit>());
    func(make_shared<listbalances>());
    func(make_shared<getbalance>());
    func(make_shared<getaddressetp>());

    os <<"\r\n";
    // asset
    func(make_shared<createasset>());
    func(make_shared<deletelocalasset>());
    func(make_shared<issue>());
    func(make_shared<secondaryissue>());
    func(make_shared<sendasset>());
    func(make_shared<sendassetfrom>());
    func(make_shared<listassets>());
    func(make_shared<getasset>());
    func(make_shared<getaccountasset>());
    // func(make_shared<getassetview>());
    func(make_shared<getaddressasset>());
    func(make_shared<burn>());
    func(make_shared<swaptoken>());

    os <<"\r\n";
    // cert
    func(make_shared<issuecert>());
    func(make_shared<transfercert>());

    os <<"\r\n";
    // mit
    func(make_shared<registermit>());
    func(make_shared<transfermit>());
    func(make_shared<listmits>());
    func(make_shared<getmit>());

    os <<"\r\n";
    //did
    func(make_shared<registerdid>());
    func(make_shared<didchangeaddress>());
    func(make_shared<listdids>());
    func(make_shared<getdid>());
}

shared_ptr<command> find_extension(const string& symbol)
{
    using namespace std;
    using namespace commands;

    // account
    if (symbol == getnewaccount::symbol())
        return make_shared<getnewaccount>();
    if (symbol == getaccount::symbol())
        return make_shared<getaccount>();
    if (symbol == deleteaccount::symbol())
        return make_shared<deleteaccount>();
    if (symbol == changepasswd::symbol())
        return make_shared<changepasswd>();
    if (symbol == validateaddress::symbol())
        return make_shared<validateaddress>();
    if (symbol == getnewaddress::symbol())
        return make_shared<getnewaddress>();
    if (symbol == listaddresses::symbol())
        return make_shared<listaddresses>();
    if (symbol == importaccount::symbol())
        return make_shared<importaccount>();
    if (symbol == dumpkeyfile::symbol() || symbol == "exportaccountasfile")
        return make_shared<dumpkeyfile>();
    if (symbol == importkeyfile::symbol() || symbol == "importaccountfromfile")
        return make_shared<importkeyfile>();

    // system
    if (symbol == shutdown::symbol())
        return make_shared<shutdown>();
    if (symbol == getinfo::symbol())
        return make_shared<getinfo>();
    if (symbol == addnode::symbol())
        return make_shared<addnode>();
    if (symbol == getpeerinfo::symbol())
        return make_shared<getpeerinfo>();

    // mining
    if (symbol == stopmining::symbol() || symbol == "stop")
        return make_shared<stopmining>();
    if (symbol == startmining::symbol() || symbol == "start")
        return make_shared<startmining>();
    if (symbol == setminingaccount::symbol())
        return make_shared<setminingaccount>();
    if (symbol == getmininginfo::symbol())
        return make_shared<getmininginfo>();
    if ((symbol == getwork::symbol()) || (symbol == "eth_getWork"))
        return make_shared<getwork>();
    if ((symbol == submitwork::symbol()) || ( symbol == "eth_submitWork"))
        return make_shared<submitwork>();
    if (symbol == getmemorypool::symbol())
        return make_shared<getmemorypool>();

    // block & tx
    if (symbol == getheight::symbol())
        return make_shared<getheight>();
    if (symbol == "fetch-height")
        return make_shared<getheight>(symbol);
    if (symbol == getblock::symbol())
        return make_shared<getblock>();
    if (symbol == "getbestblockhash")
        return make_shared<getblockheader>(symbol);
    if (symbol == getblockheader::symbol() || symbol == "fetch-header" || symbol == "getbestblockheader")
        return make_shared<getblockheader>();
    if (symbol == fetchheaderext::symbol())
        return make_shared<fetchheaderext>();
    if (symbol == gettx::symbol() || symbol == "gettransaction")
        return make_shared<gettx>();
    if (symbol == "fetch-tx")
        return make_shared<gettx>(symbol);
    if (symbol == listtxs::symbol())
        return make_shared<listtxs>();

    // raw tx
    if (symbol == createrawtx::symbol())
        return make_shared<createrawtx>();
    if (symbol == decoderawtx::symbol())
        return make_shared<decoderawtx>();
    if (symbol == signrawtx::symbol())
        return make_shared<signrawtx>();
    if (symbol == sendrawtx::symbol())
        return make_shared<sendrawtx>();

    // multi-sig
    if (symbol == getpublickey::symbol())
        return make_shared<getpublickey>();
    if (symbol == getnewmultisig::symbol())
        return make_shared<getnewmultisig>();
    if (symbol == listmultisig::symbol())
        return make_shared<listmultisig>();
    if (symbol == deletemultisig::symbol())
        return make_shared<deletemultisig>();
    if (symbol == createmultisigtx::symbol())
        return make_shared<createmultisigtx>();
    if (symbol == signmultisigtx::symbol())
        return make_shared<signmultisigtx>();

    // etp
    if (symbol == listbalances::symbol())
        return make_shared<listbalances>();
    if (symbol == getbalance::symbol())
        return make_shared<getbalance>();
    if (symbol == getaddressetp::symbol() || symbol == "fetch-balance")
        return make_shared<getaddressetp>();
    if (symbol == deposit::symbol())
        return make_shared<deposit>();
    if (symbol == send::symbol() || symbol == "didsend")
        return make_shared<send>();
    if (symbol == sendmore::symbol() || symbol == "didsendmore")
        return make_shared<sendmore>();
    if (symbol == sendfrom::symbol() || symbol == "didsendfrom")
        return make_shared<sendfrom>();

    // asset
    if (symbol == createasset::symbol())
        return make_shared<createasset>();
    if (symbol == deletelocalasset::symbol() || symbol == "deleteasset" )
        return make_shared<deletelocalasset>();
    if (symbol == listassets::symbol())
        return make_shared<listassets>();
    if (symbol == getasset::symbol())
        return make_shared<getasset>();
    if (symbol == getaccountasset::symbol())
        return make_shared<getaccountasset>();
    // if (symbol == getassetview::symbol())
    //     return make_shared<getassetview>();
    if (symbol == getaddressasset::symbol())
        return make_shared<getaddressasset>();
    if (symbol == issue::symbol())
        return make_shared<issue>();
    if (symbol == secondaryissue::symbol() || (symbol == "additionalissue") )
        return make_shared<secondaryissue>();
    if (symbol == sendasset::symbol() || symbol == "didsendasset")
        return make_shared<sendasset>();
    if (symbol == sendassetfrom::symbol() || symbol == "didsendassetfrom")
        return make_shared<sendassetfrom>();
    if (symbol == burn::symbol())
        return make_shared<burn>();
    if (symbol == swaptoken::symbol())
        return make_shared<swaptoken>();

    // cert
    if (symbol == transfercert::symbol())
        return make_shared<transfercert>();
    if (symbol == issuecert::symbol())
        return make_shared<issuecert>();

    // mit
    if (symbol == registermit::symbol())
        return make_shared<registermit>();
    if (symbol == transfermit::symbol())
        return make_shared<transfermit>();
    if (symbol == listmits::symbol())
        return make_shared<listmits>();
    if (symbol == getmit::symbol())
        return make_shared<getmit>();

    // did
    if (symbol == registerdid::symbol())
        return make_shared<registerdid>();
    if (symbol == didchangeaddress::symbol())
        return make_shared<didchangeaddress>();
    if (symbol == listdids::symbol())
        return make_shared<listdids>();
    if (symbol == getdid::symbol())
        return make_shared<getdid>();

    return nullptr;
}

std::string formerly_extension(const string& former)
{
    return "";
}

} // namespace explorer
} // namespace libbitcoin

<|endoftext|>


<commit_before>/** \file   rss_subset_aggregator.cc
 *  \brief  Aggregates RSS feeds.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2021 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <algorithm>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <cinttypes>
#include <cstring>
#include "Compiler.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "EmailSender.h"
#include "FileUtil.h"
#include "HtmlUtil.h"
#include "MiscUtil.h"
#include "SqlUtil.h"
#include "StringUtil.h"
#include "SyndicationFormat.h"
#include "Template.h"
#include "TimeUtil.h"
#include "UBTools.h"
#include "VuFind.h"
#include "XmlWriter.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    ::Usage(
        "--mode=(email|rss_xml) (user_id|sender_email) subsystem_type\n"
        "If the mode is \"rss_xml\" a VuFind user_id needs to be specified, o/w an error email address should be provided.");
}

struct HarvestedRSSItem {
    SyndicationFormat::Item item_;
    std::string feed_title_;
    std::string website_url_;

    HarvestedRSSItem(const SyndicationFormat::Item item, const std::string &feed_title, const std::string &website_url)
        : item_(item), feed_title_(feed_title), website_url_(website_url) { }
};

struct ChannelDesc {
    std::string title_;
    std::string link_;

public:
    ChannelDesc(const std::string &title, const std::string &link): title_(title), link_(link) { }
};

const std::map<std::string, ChannelDesc> subsystem_type_to_channel_desc_map = {
    { "relbib", ChannelDesc("RelBib RSS Aggregator", "https://relbib.de/") },
    { "ixtheo", ChannelDesc("IxTheo RSS Aggregator", "https://ixtheo.de/") },
    { "krimdok", ChannelDesc("KrimDok RSS Aggregator", "https://krimdok.uni-tuebingen.de/") },
};

std::string GetChannelDescEntry(const std::string &subsystem_type, const std::string &entry) {
    const auto subsystem_type_and_channel_desc(subsystem_type_to_channel_desc_map.find(subsystem_type));
    if (subsystem_type_and_channel_desc == subsystem_type_to_channel_desc_map.cend())
        LOG_ERROR("unknown subsystem type \"" + subsystem_type + "\"!");

    if (entry == "title")
        return subsystem_type_and_channel_desc->second.title_;
    if (entry == "link")
        return subsystem_type_and_channel_desc->second.link_;
    LOG_ERROR("unknown entry name \"" + entry + "\"!");
}

void WriteRSSFeedXMLOutput(const std::string &subsystem_type, const std::vector<HarvestedRSSItem> &harvested_items,
                           XmlWriter * const xml_writer) {
    xml_writer->openTag("rss", { { "version", "2.0" } });
    xml_writer->openTag("channel");
    xml_writer->writeTagsWithData("title", GetChannelDescEntry(subsystem_type, "title"));
    xml_writer->writeTagsWithData("link", GetChannelDescEntry(subsystem_type, "link"));
    xml_writer->writeTagsWithData("description", "RSS Aggregator");

    for (const auto &harvested_item : harvested_items) {
        xml_writer->openTag("item");

        const auto title(harvested_item.item_.getTitle());
        if (not title.empty())
            xml_writer->writeTagsWithData("title", harvested_item.item_.getTitle());

        xml_writer->writeTagsWithData("link", harvested_item.item_.getLink());

        const auto description(HtmlUtil::ShortenText(harvested_item.item_.getDescription(), /*max_length = */ 500));
        if (not description.empty())
            xml_writer->writeTagsWithData("description", description);

        xml_writer->writeTagsWithData("pubDate",
                                      TimeUtil::TimeTToString(harvested_item.item_.getPubDate(), TimeUtil::RFC822_FORMAT, TimeUtil::UTC));
        xml_writer->writeTagsWithData("guid", harvested_item.item_.getId());
        xml_writer->closeTag("item", /* suppress_indent */ false);
    }

    xml_writer->closeTag("channel");
    xml_writer->closeTag("rss");
}

struct FeedNameAndURL {
    std::string name_;
    std::string url_;

public:
    FeedNameAndURL() = default;
    FeedNameAndURL(const FeedNameAndURL &other) = default;
    FeedNameAndURL(const std::string &name, const std::string &url): name_(name), url_(url) { }
};

// \return True if a notification email was sent successfully, o/w false.
bool SendEmail(const std::string &subsystem_type, const std::string &email_sender, const std::string &user_email,
               const std::string &user_address, const std::string &language, const std::vector<HarvestedRSSItem> &harvested_items) {
    const auto template_filename_prefix(UBTools::GetTuelibPath() + "rss_email.template");
    std::string template_filename(template_filename_prefix + "." + language);
    if (not FileUtil::Exists(template_filename))
        template_filename = template_filename_prefix + ".en";
    static const std::string email_template(FileUtil::ReadStringOrDie(template_filename));

    std::string list("
\n");
    std::string previous_feed_title;
    for (const auto &harvested_item : harvested_items) {
        const bool new_feed(previous_feed_title != harvested_item.feed_title_);
        if (new_feed) {
            if (not previous_feed_title.empty()) { // not before the first feed
                list += "\t
\n"; // end feed item list
            }
            list += "\t
" + HtmlUtil::HtmlEscape(harvested_item.feed_title_) + "
\n";
            list += "\t
\n"; // begin feed item list
        }

        list += "\t\t
" + HtmlUtil::HtmlEscape(harvested_item.item_.getTitle()) + "
\n";
        previous_feed_title = harvested_item.feed_title_;
    }
    list += "\t
\n"; // end feed item list
    list += "
\n"; // end whole list

    Template::Map names_to_values_map;
    names_to_values_map.insertScalar("user_name", user_address);
    names_to_values_map.insertScalar("list", list);
    names_to_values_map.insertScalar("system", VuFind::CapitalizedUserType(subsystem_type));
    names_to_values_map.insertScalar("email_reply_to", subsystem_type + "@ub.uni-tuebingen.de");

    const auto email_body(Template::ExpandTemplate(email_template, names_to_values_map));
    const auto retcode(EmailSender::SimplerSendEmail(email_sender, { user_email }, GetChannelDescEntry(subsystem_type, "title"), email_body,
                                                     EmailSender::DO_NOT_SET_PRIORITY, EmailSender::HTML));
    if (retcode <= 299)
        return true;

    LOG_WARNING("EmailSender::SimplerSendEmail returned " + std::to_string(retcode) + " while trying to send to \"" + user_email + "\"!");
    return false;
}

const unsigned DEFAULT_XML_INDENT_AMOUNT(2);

void GenerateFeed(const std::string &subsystem_type, const std::vector<HarvestedRSSItem> &harvested_items) {
    XmlWriter xml_writer(FileUtil::OpenOutputFileOrDie("/dev/stdout").release(), XmlWriter::WriteTheXmlDeclaration,
                         DEFAULT_XML_INDENT_AMOUNT);
    WriteRSSFeedXMLOutput(subsystem_type, harvested_items, &xml_writer);
}

bool ProcessFeeds(const std::string &user_id, const std::string &rss_feed_last_notification, const std::string &email_sender,
                  const std::string &user_email, const std::string &user_address, const std::string &language, const bool send_email,
                  const std::string &subsystem_type, DbConnection * const db_connection) {
    db_connection->queryOrDie(
        "SELECT rss_feeds_id, feed_name, website_url "
        "FROM tuefind_rss_subscriptions "
        "LEFT JOIN tuefind_rss_feeds ON tuefind_rss_subscriptions.rss_feeds_id = tuefind_rss_feeds.id "
        "WHERE tuefind_rss_subscriptions.user_id=" + user_id + " "
        "ORDER BY feed_name ASC "
    );
    auto feeds_result_set(db_connection->getLastResultSet());
    std::vector<HarvestedRSSItem> harvested_items;
    std::string max_insertion_time;

    while (const auto feed_row = feeds_result_set.getNextRow()) {
        const auto feed_id(feed_row["rss_feeds_id"]);
        const auto feed_name(feed_row["feed_name"]);
        const auto website_url(feed_row["website_url"]);

        std::string query(
            "SELECT item_title,item_description,item_url,item_id,pub_date,insertion_time FROM "
            "tuefind_rss_items WHERE rss_feeds_id="
            + feed_id);
        if (send_email)
            query += " AND insertion_time > '" + rss_feed_last_notification + "' ";
        query += " ORDER BY pub_date ASC";
        db_connection->queryOrDie(query);

        auto items_result_set(db_connection->getLastResultSet());
        while (const auto item_row = items_result_set.getNextRow()) {
            harvested_items.emplace_back(SyndicationFormat::Item(item_row["item_title"], item_row["item_description"], item_row["item_url"],
                                                                 item_row["item_id"], SqlUtil::DatetimeToTimeT(item_row["pub_date"])),
                                         feed_name, website_url);
            const auto insertion_time(item_row["insertion_time"]);
            if (insertion_time > max_insertion_time)
                max_insertion_time = insertion_time;
        }
    }

    if (send_email) {
        if (harvested_items.empty())
            return false;
        if (not SendEmail(subsystem_type, email_sender, user_email, user_address, language, harvested_items))
            return true;
        db_connection->queryOrDie("UPDATE user SET tuefind_rss_feed_last_notification='" + max_insertion_time + "' WHERE id=" + user_id);
    } else
        GenerateFeed(subsystem_type, harvested_items);

    return true;
}

} // unnamed namespace

struct UserInfo {
    std::string user_id_;
    std::string first_name_;
    std::string last_name_;
    std::string email_;
    std::string rss_feed_last_notification_;
    std::string language_code_;

public:
    UserInfo() = default;
    UserInfo(const UserInfo &other) = default;
    UserInfo(const std::string &user_id, const std::string &first_name, const std::string &last_name, const std::string &email,
             const std::string &rss_feed_last_notification, const std::string &language_code)
        : user_id_(user_id), first_name_(first_name), last_name_(last_name), email_(email),
          rss_feed_last_notification_(rss_feed_last_notification), language_code_(language_code) { }
};

int Main(int argc, char *argv[]) {
    if (argc != 4)
        Usage();

    std::string sender_email, vufind_user_id;
    if (std::strcmp(argv[1], "--mode=email") == 0)
        sender_email = argv[2];
    else if (std::strcmp(argv[1], "--mode=rss_xml") == 0)
        vufind_user_id = argv[2];
    else
        Usage();
    const std::string subsystem_type(argv[3]);
    if (subsystem_type != "ixtheo" and subsystem_type != "relbib" and subsystem_type != "krimdok")
        LOG_ERROR("subsystem_type must be one of {ixtheo,relbib,krimdok}!");

    auto db_connection(DbConnection::VuFindMySQLFactory());

    std::string sql_query(
        "SELECT id,firstname,lastname,email,tuefind_rss_feed_send_emails"
        ",tuefind_rss_feed_last_notification,last_language FROM user");
    if (vufind_user_id.empty())
        sql_query += " WHERE tuefind_rss_feed_send_emails IS TRUE";
    else
        sql_query += " WHERE id=" + db_connection.escapeAndQuoteString(vufind_user_id);
    db_connection.queryOrDie(sql_query);

    auto user_result_set(db_connection.getLastResultSet());
    std::unordered_map<std::string, UserInfo> ids_to_user_infos_map;
    while (const auto user_row = user_result_set.getNextRow()) {
        const std::string last_language(user_row["last_language"]);
        ids_to_user_infos_map[user_row["id"]] =
            UserInfo(user_row["id"], user_row["firstname"], user_row["lastname"], user_row["email"],
                     user_row["tuefind_rss_feed_last_notification"], (last_language.empty() ? "en" : last_language));
    }

    unsigned feed_generation_count(0), email_sent_count(0);
    for (const auto &[user_id, user_info] : ids_to_user_infos_map) {
        if (vufind_user_id.empty() and not EmailSender::IsValidEmailAddress(user_info.email_)) {
            LOG_WARNING("no valid email address for vufind.user.id " + user_id + "!");
            continue;
        }

        if (ProcessFeeds(user_id, user_info.rss_feed_last_notification_, sender_email, user_info.email_,
                         MiscUtil::GenerateAddress(user_info.first_name_, user_info.last_name_, "Subscriber"), user_info.language_code_,
                         vufind_user_id.empty(), subsystem_type, &db_connection))
        {
            if (vufind_user_id.empty())
                ++email_sent_count;
            ++feed_generation_count;
        }
    }
    LOG_INFO("Generated " + std::to_string(feed_generation_count) + " RSS feed(s) and sent " + std::to_string(email_sent_count)
             + " email(s).");

    return EXIT_SUCCESS;
}
<commit_msg>extended query for rss to use subsystem for users, see tuefind/#2118<commit_after>/** \file   rss_subset_aggregator.cc
 *  \brief  Aggregates RSS feeds.
 *  \author Dr. Johannes Ruscheinski (johannes.ruscheinski@uni-tuebingen.de)
 *
 *  \copyright 2021 Universitätsbibliothek Tübingen.  All rights reserved.
 *
 *  This program is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU Affero General Public License as
 *  published by the Free Software Foundation, either version 3 of the
 *  License, or (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU Affero General Public License for more details.
 *
 *  You should have received a copy of the GNU Affero General Public License
 *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
#include <algorithm>
#include <map>
#include <unordered_map>
#include <unordered_set>
#include <cinttypes>
#include <cstring>
#include "Compiler.h"
#include "DbConnection.h"
#include "DbResultSet.h"
#include "EmailSender.h"
#include "FileUtil.h"
#include "HtmlUtil.h"
#include "MiscUtil.h"
#include "SqlUtil.h"
#include "StringUtil.h"
#include "SyndicationFormat.h"
#include "Template.h"
#include "TimeUtil.h"
#include "UBTools.h"
#include "VuFind.h"
#include "XmlWriter.h"
#include "util.h"

namespace {

[[noreturn]] void Usage() {
    ::Usage(
        "--mode=(email|rss_xml) (user_id|sender_email) subsystem_type\n"
        "If the mode is \"rss_xml\" a VuFind user_id needs to be specified, o/w an error email address should be provided.");
}

struct HarvestedRSSItem {
    SyndicationFormat::Item item_;
    std::string feed_title_;
    std::string website_url_;

    HarvestedRSSItem(const SyndicationFormat::Item item, const std::string &feed_title, const std::string &website_url)
        : item_(item), feed_title_(feed_title), website_url_(website_url) { }
};

struct ChannelDesc {
    std::string title_;
    std::string link_;

public:
    ChannelDesc(const std::string &title, const std::string &link): title_(title), link_(link) { }
};

const std::map<std::string, ChannelDesc> subsystem_type_to_channel_desc_map = {
    { "relbib", ChannelDesc("RelBib RSS Aggregator", "https://relbib.de/") },
    { "ixtheo", ChannelDesc("IxTheo RSS Aggregator", "https://ixtheo.de/") },
    { "krimdok", ChannelDesc("KrimDok RSS Aggregator", "https://krimdok.uni-tuebingen.de/") },
};

std::string GetChannelDescEntry(const std::string &subsystem_type, const std::string &entry) {
    const auto subsystem_type_and_channel_desc(subsystem_type_to_channel_desc_map.find(subsystem_type));
    if (subsystem_type_and_channel_desc == subsystem_type_to_channel_desc_map.cend())
        LOG_ERROR("unknown subsystem type \"" + subsystem_type + "\"!");

    if (entry == "title")
        return subsystem_type_and_channel_desc->second.title_;
    if (entry == "link")
        return subsystem_type_and_channel_desc->second.link_;
    LOG_ERROR("unknown entry name \"" + entry + "\"!");
}

void WriteRSSFeedXMLOutput(const std::string &subsystem_type, const std::vector<HarvestedRSSItem> &harvested_items,
                           XmlWriter * const xml_writer) {
    xml_writer->openTag("rss", { { "version", "2.0" } });
    xml_writer->openTag("channel");
    xml_writer->writeTagsWithData("title", GetChannelDescEntry(subsystem_type, "title"));
    xml_writer->writeTagsWithData("link", GetChannelDescEntry(subsystem_type, "link"));
    xml_writer->writeTagsWithData("description", "RSS Aggregator");

    for (const auto &harvested_item : harvested_items) {
        xml_writer->openTag("item");

        const auto title(harvested_item.item_.getTitle());
        if (not title.empty())
            xml_writer->writeTagsWithData("title", harvested_item.item_.getTitle());

        xml_writer->writeTagsWithData("link", harvested_item.item_.getLink());

        const auto description(HtmlUtil::ShortenText(harvested_item.item_.getDescription(), /*max_length = */ 500));
        if (not description.empty())
            xml_writer->writeTagsWithData("description", description);

        xml_writer->writeTagsWithData("pubDate",
                                      TimeUtil::TimeTToString(harvested_item.item_.getPubDate(), TimeUtil::RFC822_FORMAT, TimeUtil::UTC));
        xml_writer->writeTagsWithData("guid", harvested_item.item_.getId());
        xml_writer->closeTag("item", /* suppress_indent */ false);
    }

    xml_writer->closeTag("channel");
    xml_writer->closeTag("rss");
}

struct FeedNameAndURL {
    std::string name_;
    std::string url_;

public:
    FeedNameAndURL() = default;
    FeedNameAndURL(const FeedNameAndURL &other) = default;
    FeedNameAndURL(const std::string &name, const std::string &url): name_(name), url_(url) { }
};

// \return True if a notification email was sent successfully, o/w false.
bool SendEmail(const std::string &subsystem_type, const std::string &email_sender, const std::string &user_email,
               const std::string &user_address, const std::string &language, const std::vector<HarvestedRSSItem> &harvested_items) {
    const auto template_filename_prefix(UBTools::GetTuelibPath() + "rss_email.template");
    std::string template_filename(template_filename_prefix + "." + language);
    if (not FileUtil::Exists(template_filename))
        template_filename = template_filename_prefix + ".en";
    static const std::string email_template(FileUtil::ReadStringOrDie(template_filename));

    std::string list("
\n");
    std::string previous_feed_title;
    for (const auto &harvested_item : harvested_items) {
        const bool new_feed(previous_feed_title != harvested_item.feed_title_);
        if (new_feed) {
            if (not previous_feed_title.empty()) { // not before the first feed
                list += "\t
\n"; // end feed item list
            }
            list += "\t
" + HtmlUtil::HtmlEscape(harvested_item.feed_title_) + "
\n";
            list += "\t
\n"; // begin feed item list
        }

        list += "\t\t
" + HtmlUtil::HtmlEscape(harvested_item.item_.getTitle()) + "
\n";
        previous_feed_title = harvested_item.feed_title_;
    }
    list += "\t
\n"; // end feed item list
    list += "
\n"; // end whole list

    Template::Map names_to_values_map;
    names_to_values_map.insertScalar("user_name", user_address);
    names_to_values_map.insertScalar("list", list);
    names_to_values_map.insertScalar("system", VuFind::CapitalizedUserType(subsystem_type));
    names_to_values_map.insertScalar("email_reply_to", subsystem_type + "@ub.uni-tuebingen.de");

    const auto email_body(Template::ExpandTemplate(email_template, names_to_values_map));
    const auto retcode(EmailSender::SimplerSendEmail(email_sender, { user_email }, GetChannelDescEntry(subsystem_type, "title"), email_body,
                                                     EmailSender::DO_NOT_SET_PRIORITY, EmailSender::HTML));
    if (retcode <= 299)
        return true;

    LOG_WARNING("EmailSender::SimplerSendEmail returned " + std::to_string(retcode) + " while trying to send to \"" + user_email + "\"!");
    return false;
}

const unsigned DEFAULT_XML_INDENT_AMOUNT(2);

void GenerateFeed(const std::string &subsystem_type, const std::vector<HarvestedRSSItem> &harvested_items) {
    XmlWriter xml_writer(FileUtil::OpenOutputFileOrDie("/dev/stdout").release(), XmlWriter::WriteTheXmlDeclaration,
                         DEFAULT_XML_INDENT_AMOUNT);
    WriteRSSFeedXMLOutput(subsystem_type, harvested_items, &xml_writer);
}

bool ProcessFeeds(const std::string &user_id, const std::string &rss_feed_last_notification, const std::string &email_sender,
                  const std::string &user_email, const std::string &user_address, const std::string &language, const bool send_email,
                  const std::string &subsystem_type, DbConnection * const db_connection) {
    db_connection->queryOrDie(
        "SELECT rss_feeds_id, feed_name, website_url "
        "FROM tuefind_rss_subscriptions "
        "LEFT JOIN tuefind_rss_feeds ON tuefind_rss_subscriptions.rss_feeds_id = tuefind_rss_feeds.id "
        "WHERE tuefind_rss_subscriptions.user_id=" + user_id + " "
        "ORDER BY feed_name ASC "
    );
    auto feeds_result_set(db_connection->getLastResultSet());
    std::vector<HarvestedRSSItem> harvested_items;
    std::string max_insertion_time;

    while (const auto feed_row = feeds_result_set.getNextRow()) {
        const auto feed_id(feed_row["rss_feeds_id"]);
        const auto feed_name(feed_row["feed_name"]);
        const auto website_url(feed_row["website_url"]);

        std::string query(
            "SELECT item_title,item_description,item_url,item_id,pub_date,insertion_time FROM "
            "tuefind_rss_items WHERE rss_feeds_id="
            + feed_id);
        if (send_email)
            query += " AND insertion_time > '" + rss_feed_last_notification + "' ";
        query += " ORDER BY pub_date ASC";
        db_connection->queryOrDie(query);

        auto items_result_set(db_connection->getLastResultSet());
        while (const auto item_row = items_result_set.getNextRow()) {
            harvested_items.emplace_back(SyndicationFormat::Item(item_row["item_title"], item_row["item_description"], item_row["item_url"],
                                                                 item_row["item_id"], SqlUtil::DatetimeToTimeT(item_row["pub_date"])),
                                         feed_name, website_url);
            const auto insertion_time(item_row["insertion_time"]);
            if (insertion_time > max_insertion_time)
                max_insertion_time = insertion_time;
        }
    }

    if (send_email) {
        if (harvested_items.empty())
            return false;
        if (not SendEmail(subsystem_type, email_sender, user_email, user_address, language, harvested_items))
            return true;
        db_connection->queryOrDie("UPDATE user SET tuefind_rss_feed_last_notification='" + max_insertion_time + "' WHERE id=" + user_id);
    } else
        GenerateFeed(subsystem_type, harvested_items);

    return true;
}

} // unnamed namespace

struct UserInfo {
    std::string user_id_;
    std::string first_name_;
    std::string last_name_;
    std::string email_;
    std::string rss_feed_last_notification_;
    std::string language_code_;

public:
    UserInfo() = default;
    UserInfo(const UserInfo &other) = default;
    UserInfo(const std::string &user_id, const std::string &first_name, const std::string &last_name, const std::string &email,
             const std::string &rss_feed_last_notification, const std::string &language_code)
        : user_id_(user_id), first_name_(first_name), last_name_(last_name), email_(email),
          rss_feed_last_notification_(rss_feed_last_notification), language_code_(language_code) { }
};

int Main(int argc, char *argv[]) {
    if (argc != 4)
        Usage();

    std::string sender_email, vufind_user_id;
    if (std::strcmp(argv[1], "--mode=email") == 0)
        sender_email = argv[2];
    else if (std::strcmp(argv[1], "--mode=rss_xml") == 0)
        vufind_user_id = argv[2];
    else
        Usage();
    const std::string subsystem_type(argv[3]);
    if (subsystem_type != "ixtheo" and subsystem_type != "relbib" and subsystem_type != "krimdok")
        LOG_ERROR("subsystem_type must be one of {ixtheo,relbib,krimdok}!");

    auto db_connection(DbConnection::VuFindMySQLFactory());

    std::string sql_query(
        "SELECT id,firstname,lastname,email,tuefind_rss_feed_send_emails"
        ",tuefind_rss_feed_last_notification,last_language FROM user");
    if (vufind_user_id.empty())
        sql_query += " WHERE tuefind_rss_feed_send_emails IS TRUE";
    else
        sql_query += " WHERE id=" + db_connection.escapeAndQuoteString(vufind_user_id);
    sql_query += " AND ixtheo_user_type='" + subsystem_type + "'";
    db_connection.queryOrDie(sql_query);

    auto user_result_set(db_connection.getLastResultSet());
    std::unordered_map<std::string, UserInfo> ids_to_user_infos_map;
    while (const auto user_row = user_result_set.getNextRow()) {
        const std::string last_language(user_row["last_language"]);
        ids_to_user_infos_map[user_row["id"]] =
            UserInfo(user_row["id"], user_row["firstname"], user_row["lastname"], user_row["email"],
                     user_row["tuefind_rss_feed_last_notification"], (last_language.empty() ? "en" : last_language));
    }

    unsigned feed_generation_count(0), email_sent_count(0);
    for (const auto &[user_id, user_info] : ids_to_user_infos_map) {
        if (vufind_user_id.empty() and not EmailSender::IsValidEmailAddress(user_info.email_)) {
            LOG_WARNING("no valid email address for vufind.user.id " + user_id + "!");
            continue;
        }

        if (ProcessFeeds(user_id, user_info.rss_feed_last_notification_, sender_email, user_info.email_,
                         MiscUtil::GenerateAddress(user_info.first_name_, user_info.last_name_, "Subscriber"), user_info.language_code_,
                         vufind_user_id.empty(), subsystem_type, &db_connection))
        {
            if (vufind_user_id.empty())
                ++email_sent_count;
            ++feed_generation_count;
        }
    }
    LOG_INFO("Generated " + std::to_string(feed_generation_count) + " RSS feed(s) and sent " + std::to_string(email_sent_count)
             + " email(s).");

    return EXIT_SUCCESS;
}
<|endoftext|>


<commit_before>///////////////////////////////////////////////////////////////////////////////
// droppable_accumulator.hpp
//
//  Copyright 2005 Eric Niebler. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_DROPPABLE_ACCUMULATOR_HPP_EAN_13_12_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_DROPPABLE_ACCUMULATOR_HPP_EAN_13_12_2005

#include <new>
#include <boost/assert.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/aligned_storage.hpp>
#include <boost/accumulators/framework/depends_on.hpp> // for feature_of
#include <boost/accumulators/framework/parameters/accumulator.hpp> // for accumulator

namespace pdalboost{} namespace boost = pdalboost; namespace pdalboost{ namespace accumulators
{

    template<typename Accumulator>
    struct droppable_accumulator;

    namespace detail
    {
        ///////////////////////////////////////////////////////////////////////////////
        // add_ref_visitor
        //   a fusion function object for add_ref'ing accumulators
        template<typename Args>
        struct add_ref_visitor
        {
            explicit add_ref_visitor(Args const &args)
              : args_(args)
            {
            }

            template<typename Accumulator>
            void operator ()(Accumulator &acc) const
            {
                typedef typename Accumulator::feature_tag::dependencies dependencies;

                acc.add_ref(this->args_);

                // Also add_ref accumulators that this feature depends on
                this->args_[accumulator].template
                    visit_if<detail::contains_feature_of_<dependencies> >(
                        *this
                );
            }

        private:
            add_ref_visitor &operator =(add_ref_visitor const &);
            Args const &args_;
        };

        template<typename Args>
        add_ref_visitor<Args> make_add_ref_visitor(Args const &args)
        {
            return add_ref_visitor<Args>(args);
        }

        ///////////////////////////////////////////////////////////////////////////////
        // drop_visitor
        //   a fusion function object for dropping accumulators
        template<typename Args>
        struct drop_visitor
        {
            explicit drop_visitor(Args const &args)
              : args_(args)
            {
            }

            template<typename Accumulator>
            void operator ()(Accumulator &acc) const
            {
                if(typename Accumulator::is_droppable())
                {
                    typedef typename Accumulator::feature_tag::dependencies dependencies;

                    acc.drop(this->args_);
                    // Also drop accumulators that this feature depends on
                    this->args_[accumulator].template
                        visit_if<detail::contains_feature_of_<dependencies> >(
                            *this
                    );
                }
            }

        private:
            drop_visitor &operator =(drop_visitor const &);
            Args const &args_;
        };

        template<typename Args>
        drop_visitor<Args> make_drop_visitor(Args const &args)
        {
            return drop_visitor<Args>(args);
        }
    }

    //////////////////////////////////////////////////////////////////////////
    // droppable_accumulator_base
    template<typename Accumulator>
    struct droppable_accumulator_base
      : Accumulator
    {
        typedef droppable_accumulator_base base;
        typedef mpl::true_ is_droppable;
        typedef typename Accumulator::result_type result_type;

        template<typename Args>
        droppable_accumulator_base(Args const &args)
          : Accumulator(args)
          , ref_count_(0)
        {
        }

        template<typename Args>
        void operator ()(Args const &args)
        {
            if(!this->is_dropped())
            {
                this->Accumulator::operator ()(args);
            }
        }

        template<typename Args>
        void add_ref(Args const &)
        {
            ++this->ref_count_;
        }

        template<typename Args>
        void drop(Args const &args)
        {
            BOOST_ASSERT(0 < this->ref_count_);
            if(1 == this->ref_count_)
            {
                static_cast<droppable_accumulator<Accumulator> *>(this)->on_drop(args);
            }
            --this->ref_count_;
        }

        bool is_dropped() const
        {
            return 0 == this->ref_count_;
        }

    private:
        int ref_count_;
    };

    //////////////////////////////////////////////////////////////////////////
    // droppable_accumulator
    //   this can be specialized for any type that needs special handling
    template<typename Accumulator>
    struct droppable_accumulator
      : droppable_accumulator_base<Accumulator>
    {
        template<typename Args>
        droppable_accumulator(Args const &args)
          : droppable_accumulator::base(args)
        {
        }
    };

    //////////////////////////////////////////////////////////////////////////
    // with_cached_result
    template<typename Accumulator>
    struct with_cached_result
      : Accumulator
    {
        typedef typename Accumulator::result_type result_type;

        template<typename Args>
        with_cached_result(Args const &args)
          : Accumulator(args)
          , cache()
        {
        }

        with_cached_result(with_cached_result const &that)
          : Accumulator(*static_cast<Accumulator const *>(&that))
          , cache()
        {
            if(that.has_result())
            {
                this->set(that.get());
            }
        }

        ~with_cached_result()
        {
            // Since this is a base class of droppable_accumulator_base,
            // this destructor is called before any of droppable_accumulator_base's
            // members get cleaned up, including is_dropped, so the following
            // call to has_result() is valid.
            if(this->has_result())
            {
                this->get().~result_type();
            }
        }

        template<typename Args>
        void on_drop(Args const &args)
        {
            // cache the result at the point this calcuation was dropped
            BOOST_ASSERT(!this->has_result());
            this->set(this->Accumulator::result(args));
        }

        template<typename Args>
        result_type result(Args const &args) const
        {
            return this->has_result() ? this->get() : this->Accumulator::result(args);
        }

    private:
        with_cached_result &operator =(with_cached_result const &);

        void set(result_type const &r)
        {
            ::new(this->cache.address()) result_type(r);
        }

        result_type const &get() const
        {
            return *static_cast<result_type const *>(this->cache.address());
        }

        bool has_result() const
        {
            typedef with_cached_result<Accumulator> this_type;
            typedef droppable_accumulator_base<this_type> derived_type;
            return static_cast<derived_type const *>(this)->is_dropped();
        }

        aligned_storage<sizeof(result_type)> cache;
    };

    namespace tag
    {
        template<typename Feature>
        struct as_droppable
        {
            typedef droppable<Feature> type;
        };

        template<typename Feature>
        struct as_droppable<droppable<Feature> >
        {
            typedef droppable<Feature> type;
        };

        //////////////////////////////////////////////////////////////////////////
        // droppable
        template<typename Feature>
        struct droppable
          : as_feature<Feature>::type
        {
            typedef typename as_feature<Feature>::type feature_type;
            typedef typename feature_type::dependencies tmp_dependencies_;

            typedef
                typename mpl::transform<
                    typename feature_type::dependencies
                  , as_droppable<mpl::_1>
                >::type
            dependencies;

            struct impl
            {
                template<typename Sample, typename Weight>
                struct apply
                {
                    typedef
                        droppable_accumulator<
                            typename mpl::apply2<typename feature_type::impl, Sample, Weight>::type
                        >
                    type;
                };
            };
        };
    }

    // make droppable<tag::feature(modifier)> work
    template<typename Feature>
    struct as_feature<tag::droppable<Feature> >
    {
        typedef tag::droppable<typename as_feature<Feature>::type> type;
    };

    // make droppable<tag::mean> work with non-void weights (should become
    // droppable<tag::weighted_mean>
    template<typename Feature>
    struct as_weighted_feature<tag::droppable<Feature> >
    {
        typedef tag::droppable<typename as_weighted_feature<Feature>::type> type;
    };

    // for the purposes of feature-based dependency resolution,
    // droppable<Foo> provides the same feature as Foo
    template<typename Feature>
    struct feature_of<tag::droppable<Feature> >
      : feature_of<Feature>
    {
    };

    // Note: Usually, the extractor is pulled into the accumulators namespace with
    // a using directive, not the tag. But the droppable<> feature doesn't have an
    // extractor, so we can put the droppable tag in the accumulators namespace
    // without fear of a name conflict.
    using tag::droppable;

}} // namespace pdalboost::accumulators

#endif
<commit_msg>patch for boost bug #6535 to allow compilation of boost::accumulators::droppable on msvc<commit_after>///////////////////////////////////////////////////////////////////////////////
// droppable_accumulator.hpp
//
//  Copyright 2005 Eric Niebler. Distributed under the Boost
//  Software License, Version 1.0. (See accompanying file
//  LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)

#ifndef BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_DROPPABLE_ACCUMULATOR_HPP_EAN_13_12_2005
#define BOOST_ACCUMULATORS_FRAMEWORK_ACCUMULATORS_DROPPABLE_ACCUMULATOR_HPP_EAN_13_12_2005

#include <new>
#include <boost/assert.hpp>
#include <boost/mpl/apply.hpp>
#include <boost/aligned_storage.hpp>
#include <boost/accumulators/framework/depends_on.hpp> // for feature_of
#include <boost/accumulators/framework/parameters/accumulator.hpp> // for accumulator

namespace pdalboost{} namespace boost = pdalboost; namespace pdalboost{ namespace accumulators
{

    template<typename Accumulator>
    struct droppable_accumulator;

    namespace detail
    {
        ///////////////////////////////////////////////////////////////////////////////
        // add_ref_visitor
        //   a fusion function object for add_ref'ing accumulators
        template<typename Args>
        struct add_ref_visitor
        {
            explicit add_ref_visitor(Args const &args)
              : args_(args)
            {
            }

            template<typename Accumulator>
            void operator ()(Accumulator &acc) const
            {
                typedef typename Accumulator::feature_tag::dependencies dependencies;

                acc.add_ref(this->args_);

                // Also add_ref accumulators that this feature depends on
                this->args_[accumulator].template
                    visit_if<detail::contains_feature_of_<dependencies> >(
                        *this
                );
            }

        private:
            add_ref_visitor &operator =(add_ref_visitor const &);
            Args const &args_;
        };

        template<typename Args>
        add_ref_visitor<Args> make_add_ref_visitor(Args const &args)
        {
            return add_ref_visitor<Args>(args);
        }

        ///////////////////////////////////////////////////////////////////////////////
        // drop_visitor
        //   a fusion function object for dropping accumulators
        template<typename Args>
        struct drop_visitor
        {
            explicit drop_visitor(Args const &args)
              : args_(args)
            {
            }

            template<typename Accumulator>
            void operator ()(Accumulator &acc) const
            {
                if(typename Accumulator::is_droppable())
                {
                    typedef typename Accumulator::feature_tag::dependencies dependencies;

                    acc.drop(this->args_);
                    // Also drop accumulators that this feature depends on
                    this->args_[accumulator].template
                        visit_if<detail::contains_feature_of_<dependencies> >(
                            *this
                    );
                }
            }

        private:
            drop_visitor &operator =(drop_visitor const &);
            Args const &args_;
        };

        template<typename Args>
        drop_visitor<Args> make_drop_visitor(Args const &args)
        {
            return drop_visitor<Args>(args);
        }
    }

    //////////////////////////////////////////////////////////////////////////
    // droppable_accumulator_base
    template<typename Accumulator>
    struct droppable_accumulator_base
      : Accumulator
    {
        typedef droppable_accumulator_base base;
        typedef mpl::true_ is_droppable;
        typedef typename Accumulator::result_type result_type;

        template<typename Args>
        droppable_accumulator_base(Args const &args)
          : Accumulator(args)
          , ref_count_(0)
        {
        }

        droppable_accumulator_base(droppable_accumulator_base const &that)
          : Accumulator(*static_cast<Accumulator const *>(&that))
          , ref_count_(that.ref_count_)
        {
        }

        template<typename Args>
        void operator ()(Args const &args)
        {
            if(!this->is_dropped())
            {
                this->Accumulator::operator ()(args);
            }
        }

        template<typename Args>
        void add_ref(Args const &)
        {
            ++this->ref_count_;
        }

        template<typename Args>
        void drop(Args const &args)
        {
            BOOST_ASSERT(0 < this->ref_count_);
            if(1 == this->ref_count_)
            {
                static_cast<droppable_accumulator<Accumulator> *>(this)->on_drop(args);
            }
            --this->ref_count_;
        }

        bool is_dropped() const
        {
            return 0 == this->ref_count_;
        }

    private:
        int ref_count_;
    };

    //////////////////////////////////////////////////////////////////////////
    // droppable_accumulator
    //   this can be specialized for any type that needs special handling
    template<typename Accumulator>
    struct droppable_accumulator
      : droppable_accumulator_base<Accumulator>
    {
        template<typename Args>
        droppable_accumulator(Args const &args)
          : droppable_accumulator::base(args)
        {
        }

        droppable_accumulator(droppable_accumulator const &that)
          : droppable_accumulator::base(*static_cast<droppable_accumulator::base const *>(&that))
        {
        }
    };

    //////////////////////////////////////////////////////////////////////////
    // with_cached_result
    template<typename Accumulator>
    struct with_cached_result
      : Accumulator
    {
        typedef typename Accumulator::result_type result_type;

        template<typename Args>
        with_cached_result(Args const &args)
          : Accumulator(args)
          , cache()
        {
        }

        with_cached_result(with_cached_result const &that)
          : Accumulator(*static_cast<Accumulator const *>(&that))
          , cache()
        {
            if(that.has_result())
            {
                this->set(that.get());
            }
        }

        ~with_cached_result()
        {
            // Since this is a base class of droppable_accumulator_base,
            // this destructor is called before any of droppable_accumulator_base's
            // members get cleaned up, including is_dropped, so the following
            // call to has_result() is valid.
            if(this->has_result())
            {
                this->get().~result_type();
            }
        }

        template<typename Args>
        void on_drop(Args const &args)
        {
            // cache the result at the point this calcuation was dropped
            BOOST_ASSERT(!this->has_result());
            this->set(this->Accumulator::result(args));
        }

        template<typename Args>
        result_type result(Args const &args) const
        {
            return this->has_result() ? this->get() : this->Accumulator::result(args);
        }

    private:
        with_cached_result &operator =(with_cached_result const &);

        void set(result_type const &r)
        {
            ::new(this->cache.address()) result_type(r);
        }

        result_type const &get() const
        {
            return *static_cast<result_type const *>(this->cache.address());
        }

        bool has_result() const
        {
            typedef with_cached_result<Accumulator> this_type;
            typedef droppable_accumulator_base<this_type> derived_type;
            return static_cast<derived_type const *>(this)->is_dropped();
        }

        aligned_storage<sizeof(result_type)> cache;
    };

    namespace tag
    {
        template<typename Feature>
        struct as_droppable
        {
            typedef droppable<Feature> type;
        };

        template<typename Feature>
        struct as_droppable<droppable<Feature> >
        {
            typedef droppable<Feature> type;
        };

        //////////////////////////////////////////////////////////////////////////
        // droppable
        template<typename Feature>
        struct droppable
          : as_feature<Feature>::type
        {
            typedef typename as_feature<Feature>::type feature_type;
            typedef typename feature_type::dependencies tmp_dependencies_;

            typedef
                typename mpl::transform<
                    typename feature_type::dependencies
                  , as_droppable<mpl::_1>
                >::type
            dependencies;

            struct impl
            {
                template<typename Sample, typename Weight>
                struct apply
                {
                    typedef
                        droppable_accumulator<
                            typename mpl::apply2<typename feature_type::impl, Sample, Weight>::type
                        >
                    type;
                };
            };
        };
    }

    // make droppable<tag::feature(modifier)> work
    template<typename Feature>
    struct as_feature<tag::droppable<Feature> >
    {
        typedef tag::droppable<typename as_feature<Feature>::type> type;
    };

    // make droppable<tag::mean> work with non-void weights (should become
    // droppable<tag::weighted_mean>
    template<typename Feature>
    struct as_weighted_feature<tag::droppable<Feature> >
    {
        typedef tag::droppable<typename as_weighted_feature<Feature>::type> type;
    };

    // for the purposes of feature-based dependency resolution,
    // droppable<Foo> provides the same feature as Foo
    template<typename Feature>
    struct feature_of<tag::droppable<Feature> >
      : feature_of<Feature>
    {
    };

    // Note: Usually, the extractor is pulled into the accumulators namespace with
    // a using directive, not the tag. But the droppable<> feature doesn't have an
    // extractor, so we can put the droppable tag in the accumulators namespace
    // without fear of a name conflict.
    using tag::droppable;

}} // namespace pdalboost::accumulators

#endif
<|endoftext|>


<commit_before>/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow_lite_support/cc/task/processor/image_preprocessor.h"

#include <fstream>
#include <memory>

#include "absl/flags/flag.h"
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "tensorflow/lite/core/shims/cc/shims_test_util.h"
#include "tensorflow/lite/kernels/builtin_op_kernels.h"
#include "tensorflow/lite/mutable_op_resolver.h"
#include "tensorflow_lite_support/cc/port/gmock.h"
#include "tensorflow_lite_support/cc/port/gtest.h"
#include "tensorflow_lite_support/cc/port/status_matchers.h"
#include "tensorflow_lite_support/cc/task/core/task_api_factory.h"
#include "tensorflow_lite_support/cc/task/core/task_utils.h"
#include "tensorflow_lite_support/cc/task/core/tflite_engine.h"
#include "tensorflow_lite_support/cc/task/vision/utils/frame_buffer_common_utils.h"
#include "tensorflow_lite_support/cc/test/test_utils.h"
#include "tensorflow_lite_support/examples/task/vision/desktop/utils/image_utils.h"

namespace tflite {
namespace task {
namespace processor {
namespace {

using ::testing::ElementsAreArray;
using ::testing::HasSubstr;
using ::testing::Optional;
using ::tflite::support::kTfLiteSupportPayload;
using ::tflite::support::StatusOr;
using ::tflite::support::TfLiteSupportStatus;
using ::tflite::task::JoinPath;
using ::tflite::task::core::PopulateTensor;
using ::tflite::task::core::TaskAPIFactory;
using ::tflite::task::core::TfLiteEngine;
using ::tflite::task::vision::DecodeImageFromFile;
using ::tflite::task::vision::FrameBuffer;
using ::tflite::task::vision::ImageData;

constexpr char kTestDataDirectory[] =
    "tensorflow_lite_support/cc/test/testdata/task/vision/";

constexpr char kDilatedConvolutionModelWithMetaData[] = "dilated_conv.tflite";

StatusOr<ImageData> LoadImage(std::string image_name) {
  return DecodeImageFromFile(
      JoinPath("./" /*test src dir*/, kTestDataDirectory, image_name));
}

class DynamicInputTest : public tflite_shims::testing::Test {
 public:
  void SetUp() {
    engine_ = absl::make_unique<TfLiteEngine>();
    engine_->BuildModelFromFile(JoinPath("./", kTestDataDirectory,
                                         kDilatedConvolutionModelWithMetaData));
    engine_->InitInterpreter();

    SUPPORT_ASSERT_OK_AND_ASSIGN(preprocessor_,
                                 ImagePreprocessor::Create(engine_.get(), {0}));
  }

 protected:
  std::unique_ptr<ImagePreprocessor> preprocessor_ = nullptr;
  std::unique_ptr<TfLiteEngine> engine_ = nullptr;
};

// See if input tensor dims signature for height and width is -1
// because it is so in the model.
TEST_F(DynamicInputTest, InputHeightAndWidthMutable) {
  const TfLiteIntArray *input_dims_signature =
      engine_->GetInputs()[0]->dims_signature;
  EXPECT_EQ(input_dims_signature->data[1], -1);
  EXPECT_EQ(input_dims_signature->data[2], -1);
}

// See if output tensor has been re-dimmed as per the input
// tensor. Expected shape: (1, input_height, input_width, 16).
TEST_F(DynamicInputTest, OutputDimensionCheck) {
  SUPPORT_ASSERT_OK_AND_ASSIGN(ImageData image, LoadImage("burger.jpg"));
  std::unique_ptr<FrameBuffer> image_frame_buffer = CreateFromRgbRawBuffer(
      image.pixel_data, FrameBuffer::Dimension{image.width, image.height});

  preprocessor_->Preprocess(*image_frame_buffer);
  absl::Status status = engine_->interpreter_wrapper()->InvokeWithoutFallback();
  EXPECT_TRUE(status.ok());
  EXPECT_EQ(engine_->GetOutputs()[0]->dims->data[0], 1);
  EXPECT_EQ(engine_->GetOutputs()[0]->dims->data[1],
            engine_->GetInputs()[0]->dims->data[1]);
  EXPECT_EQ(engine_->GetOutputs()[0]->dims->data[2],
            engine_->GetInputs()[0]->dims->data[2]);
  EXPECT_EQ(engine_->GetOutputs()[0]->dims->data[3], 16);
}

// Compare pre-processed input with an already pre-processed
// golden image.
TEST_F(DynamicInputTest, GoldenImageComparison) {
  SUPPORT_ASSERT_OK_AND_ASSIGN(ImageData image, LoadImage("burger.jpg"));
  std::unique_ptr<FrameBuffer> image_frame_buffer = CreateFromRgbRawBuffer(
      image.pixel_data, FrameBuffer::Dimension{image.width, image.height});

  preprocessor_->Preprocess(*image_frame_buffer);

  // Check the processed input image.
  float *processed_input_data =
      tflite::task::core::AssertAndReturnTypedTensor<float>(
          engine_->GetInputs()[0]);

  bool is_equal = true;
  float epsilon = 0.1f;
  std::string file_path =
      JoinPath("./", kTestDataDirectory, "burger_normalized.bin");

  std::ifstream golden_image(file_path, std::ios::binary);
  // Input read success check.
  is_equal &= golden_image.peek() != std::ifstream::traits_type::eof();

  std::vector<uint8_t> buffer(std::istreambuf_iterator<char>(golden_image), {});
  float *val_ptr = reinterpret_cast<float *>(buffer.data());

  for (size_t i = 0; i < buffer.size() / sizeof(float); ++i) {
    is_equal &= std::fabs(*val_ptr - *processed_input_data) <= epsilon;
    ++val_ptr;
    ++processed_input_data;
  }

  EXPECT_TRUE(is_equal);
}
}  // namespace
}  // namespace processor
}  // namespace task
}  // namespace tflite
<commit_msg>Move image processing to SetUp + Remove first test<commit_after>/* Copyright 2021 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow_lite_support/cc/task/processor/image_preprocessor.h"

#include <fstream>
#include <memory>

#include "absl/flags/flag.h"
#include "absl/status/status.h"
#include "absl/strings/cord.h"
#include "tensorflow/lite/core/shims/cc/shims_test_util.h"
#include "tensorflow/lite/kernels/builtin_op_kernels.h"
#include "tensorflow/lite/mutable_op_resolver.h"
#include "tensorflow_lite_support/cc/port/gmock.h"
#include "tensorflow_lite_support/cc/port/gtest.h"
#include "tensorflow_lite_support/cc/port/status_matchers.h"
#include "tensorflow_lite_support/cc/task/core/task_api_factory.h"
#include "tensorflow_lite_support/cc/task/core/task_utils.h"
#include "tensorflow_lite_support/cc/task/core/tflite_engine.h"
#include "tensorflow_lite_support/cc/task/vision/utils/frame_buffer_common_utils.h"
#include "tensorflow_lite_support/cc/test/test_utils.h"
#include "tensorflow_lite_support/examples/task/vision/desktop/utils/image_utils.h"

namespace tflite {
namespace task {
namespace processor {
namespace {

using ::tflite::support::kTfLiteSupportPayload;
using ::tflite::support::StatusOr;
using ::tflite::support::TfLiteSupportStatus;
using ::tflite::task::JoinPath;
using ::tflite::task::core::TfLiteEngine;
using ::tflite::task::vision::DecodeImageFromFile;
using ::tflite::task::vision::FrameBuffer;
using ::tflite::task::vision::ImageData;

constexpr char kTestDataDirectory[] =
    "tensorflow_lite_support/cc/test/testdata/task/vision/";

constexpr char kDilatedConvolutionModelWithMetaData[] = "dilated_conv.tflite";

StatusOr<ImageData> LoadImage(std::string image_name) {
  return DecodeImageFromFile(
      JoinPath("./" /*test src dir*/, kTestDataDirectory, image_name));
}

class DynamicInputTest : public tflite_shims::testing::Test {
 public:
  void SetUp() {
    engine_ = absl::make_unique<TfLiteEngine>();
    engine_->BuildModelFromFile(JoinPath("./", kTestDataDirectory,
                                         kDilatedConvolutionModelWithMetaData));
    engine_->InitInterpreter();

    SUPPORT_ASSERT_OK_AND_ASSIGN(auto preprocessor,
                                 ImagePreprocessor::Create(engine_.get(), {0}));

    SUPPORT_ASSERT_OK_AND_ASSIGN(ImageData image, LoadImage("burger.jpg"));
    std::unique_ptr<FrameBuffer> image_frame_buffer = CreateFromRgbRawBuffer(
        image.pixel_data, FrameBuffer::Dimension{image.width, image.height});

    preprocessor->Preprocess(*image_frame_buffer);
  }

 protected:
  std::unique_ptr<TfLiteEngine> engine_ = nullptr;
};

// See if output tensor has been re-dimmed as per the input
// tensor. Expected shape: (1, input_height, input_width, 16).
TEST_F(DynamicInputTest, OutputDimensionCheck) {
  absl::Status status = engine_->interpreter_wrapper()->InvokeWithoutFallback();
  EXPECT_TRUE(status.ok());
  EXPECT_EQ(engine_->GetOutputs()[0]->dims->data[0], 1);
  EXPECT_EQ(engine_->GetOutputs()[0]->dims->data[1],
            engine_->GetInputs()[0]->dims->data[1]);
  EXPECT_EQ(engine_->GetOutputs()[0]->dims->data[2],
            engine_->GetInputs()[0]->dims->data[2]);
  EXPECT_EQ(engine_->GetOutputs()[0]->dims->data[3], 16);
}

// Compare pre-processed input with an already pre-processed
// golden image.
TEST_F(DynamicInputTest, GoldenImageComparison) {
  // Check the processed input image.
  float *processed_input_data =
      tflite::task::core::AssertAndReturnTypedTensor<float>(
          engine_->GetInputs()[0]);

  bool is_equal = true;
  float epsilon = 0.1f;
  std::string file_path =
      JoinPath("./", kTestDataDirectory, "burger_normalized.bin");

  std::ifstream golden_image(file_path, std::ios::binary);
  // Input read success check.
  is_equal &= golden_image.peek() != std::ifstream::traits_type::eof();
  std::vector<uint8_t> buffer(std::istreambuf_iterator<char>(golden_image), {});
  float *val_ptr = reinterpret_cast<float *>(buffer.data());

  for (size_t i = 0; i < buffer.size() / sizeof(float); ++i) {
    is_equal &= std::fabs(*val_ptr - *processed_input_data) <= epsilon;
    ++val_ptr;
    ++processed_input_data;
  }

  EXPECT_TRUE(is_equal);
}
}  // namespace
}  // namespace processor
}  // namespace task
}  // namespace tflite
<|endoftext|>


<commit_before>// Copyright 2017 Rick van Schijndel

#include <Arduino.h>
#include <stdio.h>

#include <Bounce2.h>
#include <U8g2lib.h>

// assets
#include "logo.xbm"
#include "player.xbm"
#include "obstacle.xbm"

#include "asset.h"
#include "player.h"
#include "game.h"
#include "screen_info.h"

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

const uint8_t kButtonPin = D3;

// Assets
Asset bootup_screen(bootup_width, bootup_height, bootup_bits);
Asset player_asset(player_width, player_height, player_bits);
Asset logo_asset(bootup_width, bootup_height, bootup_bits);
Asset obstacle_asset(obstacle_width, obstacle_height, obstacle_bits);

// The display object
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);

Bounce button;

Obstacle obstacles[] = {Obstacle(obstacle_asset)};
const size_t obstacles_size = sizeof(obstacles) / sizeof(obstacles[0]);
Game game(bootup_screen, player_asset, obstacles, obstacles_size, &u8g2);

void setupButton() {
  pinMode(kButtonPin, INPUT_PULLUP);
  button.attach(kButtonPin);
  button.interval(10);
}

void setupGraphics() {
  u8g2.begin();
  u8g2.setFlipMode(0);
  u8g2.setFont(u8g2_font_artossans8_8r);
}

void setup(void) {
  setupGraphics();
  setupButton();
  Serial.begin(115200);
  delay(1000);
}

bool buttonPressed = false;

void loop(void) {
  if (button.update()) {
    buttonPressed = button.read();
  } else {
    buttonPressed = false;
  }
  game.Update(buttonPressed);
  game.Draw();
}
<commit_msg>Fix naming for bootup asset<commit_after>// Copyright 2017 Rick van Schijndel

#include <Arduino.h>
#include <stdio.h>

#include <Bounce2.h>
#include <U8g2lib.h>

// assets
#include "logo.xbm"
#include "player.xbm"
#include "obstacle.xbm"

#include "asset.h"
#include "player.h"
#include "game.h"
#include "screen_info.h"

#ifdef U8X8_HAVE_HW_SPI
#include <SPI.h>
#endif
#ifdef U8X8_HAVE_HW_I2C
#include <Wire.h>
#endif

const uint8_t kButtonPin = D3;

// Assets
Asset bootup_screen(logo_width, logo_height, logo_bits);
Asset player_asset(player_width, player_height, player_bits);
Asset obstacle_asset(obstacle_width, obstacle_height, obstacle_bits);

// The display object
U8G2_SSD1306_128X64_NONAME_F_HW_I2C u8g2(U8G2_R0);

Bounce button;

Obstacle obstacles[] = {Obstacle(obstacle_asset)};
const size_t obstacles_size = sizeof(obstacles) / sizeof(obstacles[0]);
Game game(bootup_screen, player_asset, obstacles, obstacles_size, &u8g2);

void setupButton() {
  pinMode(kButtonPin, INPUT_PULLUP);
  button.attach(kButtonPin);
  button.interval(10);
}

void setupGraphics() {
  u8g2.begin();
  u8g2.setFlipMode(0);
  u8g2.setFont(u8g2_font_artossans8_8r);
}

void setup(void) {
  setupGraphics();
  setupButton();
  Serial.begin(115200);
  delay(1000);
}

bool buttonPressed = false;

void loop(void) {
  if (button.update()) {
    buttonPressed = button.read();
  } else {
    buttonPressed = false;
  }
  game.Update(buttonPressed);
  game.Draw();
}
<|endoftext|>


<commit_before>#include "window.h"

#include <cmath>  // for sine stuff

Window::Window() : plot( QString("Spectrum Analyer") ), gain(5), count(0) // <-- 'c++ initialisation list' - google it!
{
	// set up the gain knob
//	knob.setValue(gain);

	help_text.setReadOnly(1);
	help_text.setText(infoStr);
	playBtn.setText("Play");
	stopBtn.setText("Stop");
	quitBtn.setText("Quit");

	bin_arr_pos = 4;
	spinner.setValue(bin_arr[bin_arr_pos]);
	spinner.setRange(8,1024);
	spinner.setSuffix(" bins");
	connect(&spinner, SIGNAL(valueChanged(int)), SLOT(setBins(int)) );

	// use the Qt signals/slots framework to update the gain -
	// every time the knob is moved, the setGain function will be called
	connect( &knob, SIGNAL(valueChanged(double)), SLOT(setGain(double)) );

	// set up the thermometer
	thermo.setFillBrush( QBrush(Qt::green) );
	thermo.setRange(0, 10);
	thermo.show();

	// set up the initial plot data
	for( int index=0; index<plotDataSize; ++index )
	{
		xData[index] = index;
		yData[index] = gain * sin( M_PI * index/15 );
	}
	for( int iindex=0; iindex<(sampRate/2); ++iindex ) {
		fft_x[iindex]=iindex;
		fft_y[iindex]=iindex;
	}

	// make a plot curve from the data and attach it to the plot
	amp_curve.setSamples(xData, yData, plotDataSize);
	amp_curve.attach(&plot);

	// don't need to fill up pre samples, can all be 0
	spec_curve.setSamples(fft_x, fft_y, sampRate/2);
	spec_curve.attach(&spec_plot);

	plot.replot();
	spec_plot.replot();

	plot.setAxisTitle(plot.xBottom,time_x);
	plot.setAxisTitle(plot.yLeft, time_y);
	spec_plot.setAxisTitle(spec_plot.xBottom,spec_x);
	spec_plot.setAxisTitle(spec_plot.yLeft,spec_y);

	plot.show();
	spec_plot.show();

	hTimeL.addWidget(&thermo);
	hTimeL.addWidget(&plot);
	hSpecL.addLayout(&vSpecOptionsL);

	hSpecL.addWidget(&spec_plot);
	vSpecOptionsL.addWidget(&spinner);
	vSpecOptionsL.addWidget(&help_text);
	vSpecOptionsL.addLayout(&vControlsL);

	hPlayPauseL.addWidget(&playBtn);
	hPlayPauseL.addWidget(&stopBtn);

	vControlsL.addLayout(&hPlayPauseL);
	vControlsL.addWidget(&quitBtn);

	vMainL.addLayout(&hTimeL);
	vMainL.addLayout(&hSpecL);

	setLayout(&vMainL);
}

void Window::timerEvent( QTimerEvent * )
{
	// generate an sine wave input for example purposes - you must get yours from the A/D!
	double inVal = gain * sin( M_PI * count/15.0 );
	++count;

	// add the new input to the plot
	memmove( yData, yData+1, (plotDataSize-1) * sizeof(double) );
	yData[plotDataSize-1] = inVal;

	amp_curve.setSamples(xData, yData, plotDataSize);
	plot.replot();

	// set the thermometer value
	thermo.setValue( inVal +5);
	if(thermo.value()>0){
		thermo.setFillBrush ( QBrush(Qt::green));
	}
	if(thermo.value()>7){
		thermo.setFillBrush ( QBrush(Qt::yellow));
	}
	if(thermo.value()>9){
		thermo.setFillBrush ( QBrush(Qt::red));
	}

	// could be method, could be threaded
	if(count > fft.fft_in_size){
		for(int i=0; i<fft.fft_in_size;i++){
			fft.fft_in[i]=	yData[i];
		}
		fft.setPlan();
		fft.executeFFT();
		for(int k=0; k<fft.fft_out_size; k++){
			fft_y[k]=fft.fft_out[k][0];
		}
		spec_curve.setSamples(fft_x,fft_y,sampRate/2);
		spec_plot.replot();
	}

}

// this function can be used to change the gain of the A/D internal amplifier
void Window::setGain(double gain)
{
	// for example purposes just change the amplitude of the generated input
	this->gain = gain;
}

void Window::setBins(int bins)
{
	if(bins>pbins){
		pbins = bin_arr[bin_arr_pos++];
	} else {
		pbins = bin_arr[bin_arr_pos--];
	}
	this->bins = pbins;
}
<commit_msg>Adding control button methods<commit_after>#include "window.h"

#include <cmath>  // for sine stuff

Window::Window() : plot( QString("Spectrum Analyer") ), gain(5), count(0) // <-- 'c++ initialisation list' - google it!
{
	// set up the gain knob
//	knob.setValue(gain);

	help_text.setReadOnly(1);
	help_text.setText(infoStr);

	playBtn.setText("Play");
	stopBtn.setText("Stop");
	quitBtn.setText("Quit");
	// either valueChanged or onClick?
	connect ( &play, SIGNAL(valueChanged(double)), SLOT(play()) );
	connect ( &stop, SIGNAL(valueChanged(double)), SLOT(stop()) );
	connect ( &quit, SIGNAL(valueChanged(double)), SLOT(quit()) );

	bin_arr_pos = 4;
	spinner.setValue(bin_arr[bin_arr_pos]);
	spinner.setRange(8,1024);
	spinner.setSuffix(" bins");
	connect(&spinner, SIGNAL(valueChanged(int)), SLOT(setBins(int)) );

	// use the Qt signals/slots framework to update the gain -
	// every time the knob is moved, the setGain function will be called
	connect( &knob, SIGNAL(valueChanged(double)), SLOT(setGain(double)) );

	// set up the thermometer
	thermo.setFillBrush( QBrush(Qt::green) );
	thermo.setRange(0, 10);
	thermo.show();

	// set up the initial plot data
	for( int index=0; index<plotDataSize; ++index )
	{
		xData[index] = index;
		yData[index] = gain * sin( M_PI * index/15 );
	}
	for( int iindex=0; iindex<(sampRate/2); ++iindex ) {
		fft_x[iindex]=iindex;
		fft_y[iindex]=0;
	}

	// make a plot curve from the data and attach it to the plot
	amp_curve.setSamples(xData, yData, plotDataSize);
	amp_curve.attach(&plot);

	// don't need to fill up pre samples, can all be 0
	spec_curve.setSamples(fft_x, fft_y, sampRate/2);
	spec_curve.attach(&spec_plot);

	plot.replot();
	spec_plot.replot();

	plot.setAxisTitle(plot.xBottom,time_x);
	plot.setAxisTitle(plot.yLeft, time_y);
	spec_plot.setAxisTitle(spec_plot.xBottom,spec_x);
	spec_plot.setAxisTitle(spec_plot.yLeft,spec_y);

	plot.show();
	spec_plot.show();

	hTimeL.addWidget(&thermo);
	hTimeL.addWidget(&plot);
	hSpecL.addLayout(&vSpecOptionsL);

	hSpecL.addWidget(&spec_plot);
	vSpecOptionsL.addWidget(&spinner);
	vSpecOptionsL.addWidget(&help_text);
	vSpecOptionsL.addLayout(&vControlsL);

	hPlayPauseL.addWidget(&playBtn);
	hPlayPauseL.addWidget(&stopBtn);

	vControlsL.addLayout(&hPlayPauseL);
	vControlsL.addWidget(&quitBtn);

	vMainL.addLayout(&hTimeL);
	vMainL.addLayout(&hSpecL);

	setLayout(&vMainL);
}

void Window::timerEvent( QTimerEvent * )
{
	// generate an sine wave input for example purposes - you must get yours from the A/D!
	double inVal = gain * sin( M_PI * count/15.0 );
	++count;

	// add the new input to the plot
	memmove( yData, yData+1, (plotDataSize-1) * sizeof(double) );
	yData[plotDataSize-1] = inVal;

	amp_curve.setSamples(xData, yData, plotDataSize);
	plot.replot();

	// set the thermometer value
	thermo.setValue( inVal +5);
	if(thermo.value()>0){
		thermo.setFillBrush ( QBrush(Qt::green));
	}
	if(thermo.value()>7){
		thermo.setFillBrush ( QBrush(Qt::yellow));
	}
	if(thermo.value()>9){
		thermo.setFillBrush ( QBrush(Qt::red));
	}

	// could be method, could be threaded
	if(count > fft.fft_in_size){
		for(int i=0; i<fft.fft_in_size;i++){
			fft.fft_in[i]=	yData[i];
		}
		fft.setPlan();
		fft.executeFFT();
		for(int k=0; k<fft.fft_out_size; k++){
			fft_y[k]=fft.fft_out[k][0];
		}
		spec_curve.setSamples(fft_x,fft_y,sampRate/2);
		spec_plot.replot();
	}

}

// this function can be used to change the gain of the A/D internal amplifier
void Window::setGain(double gain)
{
	// for example purposes just change the amplitude of the generated input
	this->gain = gain;
}

void Window::setBins(int bins)
{
	if(bins>pbins){
		pbins = bin_arr[bin_arr_pos++];
	} else {
		pbins = bin_arr[bin_arr_pos--];
	}
	this->bins = pbins;
}

void Window::quit()
{
// quits the program, destructor called
}

void Window::play()
{
// start data aquisition, can only be used if !isPlay
}

void Window::stop(){
// stop data aquisition, can only be used if isPlay
}
<|endoftext|>