#ifndef __AlertConfig_h_ #define __AlertConfig_h_ #include #include #include #include #include "../shared/DatabaseWithRetry.h" #include "BitSet.h" #include "../shared/XmlSupport.h" #include "Types.h" // This is the maximum number of alerts that are typically delivered at one // time to the real-time window. Note: This is just a default and has no // other meaning. At one time it was always the case for realtime alerts, // but that is no longer true. It was never true for other ways of querying // alerts. static const int maxRTAlertCount = 35; // These are the global settings. What filters, alert types, exchanges, etc., // are available. class AllConfigInfo : NoAssign, NoCopy { public: struct AlertInfo { std::string shortName; //std::string description; PropertyList description; PropertyList qualityFilter; std::string qualityFormat; std::string flip; PropertyList keywords; std::string direction; unsigned asBit; }; struct FilterInfo { std::string shortName; std::string description; std::string units; std::string flip; std::string keywords; }; struct PairedFilterInfo { // This is the newer and preferred way to look at filters. We have one // of these for each pair of filters, i.e. MinPrice and MaxPrice. // FilterInfo is older and still required to support some older clients. std::string baseName; std::string sql; PropertyList description; PropertyList units; std::string flip; PropertyList keywords; std::string format; std::string graphics; bool topList; bool isFilter; // Some data should always be shown. Realtime data typically is only // shown if the user has special permissions or it is old. bool alwaysShow; // Some reasonable defaults: PairedFilterInfo() : topList(false), isFilter(true), alwaysShow(false) {} }; struct ExchangeInfo { std::string shortName; // Used in the database. std::string description; // Visible to the user. std::string formName; // Used on the config window. unsigned asBit; bool userVisible() const { return !description.empty(); } }; private: BitSet _legalAlerts; BitSet _defaultAlerts; std::map< std::string, AlertInfo > _alertByShortName; std::map< unsigned, AlertInfo const * > _alertByBit; std::vector< AlertInfo const * > _alertsUserOrder; void addAlert(AlertInfo const &info, bool addToDefaul); // This is the older style of filters. In the PHP code, this is almost // completely gone. This lists each min and max filter seperately. std::map< std::string, FilterInfo > _filterByShortName; std::vector< FilterInfo const * > _filtersUserOrder; void addFilter(std::string shortName, std::string description, std::string units, std::string flip, std::string keywords); // This is the newer style. std::map< std::string, PairedFilterInfo > _pairedFilterByBaseName; std::vector< PairedFilterInfo const * > _pairedFiltersUserOrder; void addFilterInfo(PairedFilterInfo const &info); BitSet _legalExchanges; BitSet _realExchanges; BitSet _defaultExchanges; std::map< std::string, ExchangeInfo > _exchangeByFormName; std::map< unsigned, ExchangeInfo const * > _exchangeByBit; std::vector< ExchangeInfo const * > _exchangesUserOrder; void addExchange(ExchangeInfo const &info); static AllConfigInfo *_instance; void initValues(); void InitConfigInfo(); AllConfigInfo(); static bool _fixFloats; public: BitSet const &getLegalAlerts() const { return _legalAlerts; } BitSet const &getDefaultAlerts() const { return _defaultAlerts; } typedef std::vector< AlertInfo const * >::const_iterator AlertIterator; AlertIterator firstAlert() const; AlertIterator endAlert() const; AlertInfo const *getAlert(std::string shortName) const; AlertInfo const *getAlert(unsigned bit) const; typedef std::vector< FilterInfo const * >::const_iterator FilterIterator; FilterIterator firstFilter() const; FilterIterator endFilter() const; FilterInfo const *getFilter(std::string shortName) const; typedef std::vector< PairedFilterInfo const * >::const_iterator PairedFilterIterator; PairedFilterIterator firstPairedFilter() const; PairedFilterIterator endPairedFilter() const; PairedFilterInfo const *getPairedFilter(std::string baseName) const; BitSet const &getLegalExchanges() const { return _legalExchanges; } BitSet const &getRealExchanges() const { return _realExchanges; } BitSet const &getDefaultExchanges() const { return _realExchanges; } typedef std::vector< ExchangeInfo const * >::const_iterator ExchangeIterator; ExchangeIterator firstExchange() const; ExchangeIterator endExchange() const; ExchangeInfo const *getExchange(std::string formName) const; ExchangeInfo const *getExchange(unsigned bit) const; void getAllAlertTypes(XmlNode &parent) const; std::string exchangeExpression(BitSet const &exchanges) const; // Replace every comma with the empty string. static std::string removeCommas(std::string s); static std::string fixSql(std::string const &original); // This object is immutable. We only create one of these for effeciency. static AllConfigInfo const &instance(); // If you are sending queries to the MySql database, set fixFloats to true. // This avoids some round off issues. If you don't do this, some results // will be slightly off from the same query sent to fast_alert_search. // // If you directly using the fast_alert_search libraries, set fixFloats // to false. Otherwise the program will die with some messages in the log // like Unknown function: “CONVERT”. // // If you are sending requests to a different process that uses // fast_alert_search, this value doesn't matter. static void init(bool fixFloats = true); }; class PairedFilterList { private: const bool _topList; const bool _onlyFilters; std::map< std::string, AllConfigInfo::PairedFilterInfo > _byBaseName; std::vector< AllConfigInfo::PairedFilterInfo const * > _userOrder; public: PairedFilterList(UserId userId, DatabaseWithRetry &database, bool topList, bool onlyFilters); // If you send this as the user id, you will not get any custom filters // associated with any specific user, but you will get the restricted filters // (i.e. options filters) that are only available to certain users. This is // required by the fast_alert_serch project so it can do some advanced // one time processing before it gets to any specific requests from any // specific users. static const int ALLOW_RESTRICTED = -1; typedef std::vector< AllConfigInfo::PairedFilterInfo const * >::const_iterator Iterator; Iterator begin() const; Iterator end() const; AllConfigInfo::PairedFilterInfo const *get(std::string baseName) const; static std::string fixPrice(std::string const &original, std::string const &priceField); }; // Initially this is only used by the top list. However, it was later added to // the alerts. class ColumnListConfig { public: struct Mapping { // internalCode is the part that never changes and might be part of // requests from the client. An older version of this file stored just // this field in an array called _columns. std::string internalCode; // altName is part of our mapping that we tell the client about // in getInitialDescription. This is used when we send requests // to the database and when we send data to the client. An older version // of this file stored just this field in an array called _names; std::string altName; // Some data should always be shown. Realtime data typically is only // shown if the user has special permissions or it is old. bool alwaysShow; TclList debugDump() const; }; typedef std::vector< Mapping > Mappings; private: Mappings _mappings; BitSet _fullExchangeData; bool _allowNonFilterColumns; static void copyData(PropertyList &dest, MysqlResultRef &source, std::string const &fieldName); void addOneColumn(std::set< std::string > &used, std::string const &internalCode, PairedFilterList const &filters); public: ColumnListConfig(); void clear(); std::string asSaveConfig() const; void getFromConfigWindow(PropertyList const &config, PairedFilterList const &filters, bool allowNonFilterColumns); std::string selectSql(PairedFilterList const &filters) const; void getInitialDescription(XmlNode &parent, PairedFilterList const &filters) const; static void getStructureForEditor(XmlNode &parent, UserId userId, DatabaseWithRetry &database, PairedFilterList const &filters, std::string const &language); void getValuesForEditor(XmlNode &parent) const; void removeIllegalData(UserId userId, DatabaseWithRetry &database); bool empty() const { return _mappings.empty(); } void copyResult(PropertyList &destination, MysqlResultRef &source) const; // This is aimed at the OddsMaker. It has the same general purpose as // getInitialDescription(). However, the goal is to format things for // a CSV file, rather than to display on screen. The CSV file will be // reasonably easy for a human or a computer to read. Column headers // will look like "Bid Size (Shares) [BS]". headers is a map from the // code used in the database query to the column header that should appear // in the config file. void getCsvNames(PropertyList &headers, PairedFilterList const &filters) const; // This is initially aimed a the MatchingSymbolCountRequest class, but // it should be generic enough to be used in other places. ColumnListConfig::Mappings const &getMappings() const { return _mappings; } // We are allowed to show these values in columns in real time. // This is only valid after calling removeIllegalData(). BitSet const &getFullExchangeData() const { return _fullExchangeData; } }; // ClientCooke is a special part of the configuation which is generated and // used by the client. The server doesn't know or care about the details. // The server accepts the cooke from the client and then returns it to the // client unmodified. // // There is only one client cookie. The format is up to the client. // Presumably it will contain some sort of name value pairs. // // The basic structure of this class is based on ColumnListConfig, but is // much simpler. class ClientCookie { private: std::string _value; public: void clear() { _value.clear(); } // The result includes an ampersand before each name/value pair. // If the cookie is empty, this returns the empty string. Otherwise // this includes only "ccookie=" and the cookie. std::string asSaveConfig() const; // This grabs the cookie from the config string. There is only one // part to the cookie, and it's named "ccookie". The default cookie // is the empty string. void getFromConfigWindow(PropertyList const &config); // This returns the cookie as part of the metadata. This is the same // place message where we return a description of each column in the // data. // // Note that the normal rules for sending data over XML apply. This // will probably trash arbitrary binary data. If the entire string // contains valid, printable UTF-8 encoded characters, this should be // a perfect match for the input. // // This will be stored in a node named "CLIENT_COOKIE" in a property // named "VALUE". "CLIENT_COOKIE" can be found at the same level as // "COLUMNS". void getInitialDescription(XmlNode &parent) const; }; // This describes a single symbol list. It should probably be a private // class within SymbolLists. We need this primarily so that we can put the // symbol lists into a data structure. struct SymbolListDesciption { UserId ownerId; SymbolListId listId; std::string asSaveConfig() const; bool operator <(SymbolListDesciption const &other) const; }; // This describes all of a users choices related to symbol lists. // There are a lot of options // and such that are specific to the collection of symbol lists. AlertConfig // is really just a collection of different types of things. The different // things don't interact at all, so it was easy to pull this code out. // // Originally this was the only major piece of AlertConfig to be put into its // own class. Later, more things were pulled about. This was motivated // in large part by the top list config. When I had a second, similar class // I had a better idea of what might or might not be shared and reused. A // lot of the new pieces were inspired by the SymbolLists class, which worked // so well. class SymbolLists { public: enum SymbolListType { sltAll, // Show all alerts. sltOnly, // Show only alerts from the specified lists. sltExclude, // Show only alerts not found in the specified lists. sltSingle }; // Show only alerts for the one given symbol. private: std::set< SymbolListDesciption > _symbolLists; std::string _singleSymbol; SymbolListType _symbolListType; void normalize(); std::string inListsSql(UserId userId, std::string const &table, bool exclude) const; public: SymbolLists() : _symbolListType(sltAll) { } void clear(); void removeIllegalLists(UserId userId, DatabaseWithRetry &database); std::string asSaveConfigUrl() const; std::string asSaveConfig() const; void getFromConfigWindow(PropertyList config); std::string whereSql(UserId userId, std::string table = "alerts") const; void getStructureForEditor(XmlNode &parent, UserId userId, bool allowFolders, bool allowNegativeListIds, DatabaseWithRetry &database) const; void getValuesForEditor(XmlNode &parent) const; SymbolListType getSymbolListType() const; std::string const &getSingleSymbol() const { return _singleSymbol; } std::set< SymbolListDesciption > getSymbolLists() const { return _symbolLists; } }; enum AlertHistoryType { ahtRealTime, ahtShortHistory, ahtHistory }; // This encapsulates most of what you do with a configuration. These objects // store the configuration. They can convert this to and from different // external formats, including an SQL statement. class AlertConfig { public: // We often send almost identical queries one after another. We do most of // the processing to convert a configuration to an SQL statement, then we // store the intermediate result in CustomSql. Finally you can add a limit // statement and some additional parts for the where clause to produce a // string. // SqlQuery in SqlQuery.h was originally responsible for this task. I moved // the code into here while refactoring the code. The new version of the // code is much more flexible. class CustomSql { public: enum AlertIndex {aiNone, aiIgnoreSymbol, aiIgnoreType, aiUsePrimary}; private: std::string _part1; AlertIndex _alertIndex; std::string _part2; // The additonal where condition goes here. std::string _part3; std::string _limit; static std::string formatLimit(int limit); std::string alertIndexString() const; ColumnListConfig _customColumns; friend class AlertConfig; public: CustomSql() : _alertIndex(aiNone) { } // The first get() uses the value of the limit stored with setLimit(). // Note: The default limit is unusable. You have to call setLimit() or // use the second form of get() or the query will fail. std::string get(std::string where) const; std::string get(std::string where, int limit) const; std::string get(std::string where, int limit, std::string tableName) const; // getCommon() uses a very common form the where statement. The id must // be greater than startAfter and less than or equal to continueThrough. // getCommon() ignores any value set by setLimit(). std::string getCommon(AlertId startAfter, AlertId continueThrough, int limit = maxRTAlertCount) const; std::string getCommon(AlertId startAfter, AlertId continueThrough, int limit, std::string tableName) const; void setLimit(int limit); void setAlertIndex(AlertIndex alertIndex) { _alertIndex = alertIndex; } bool operator ==(CustomSql const &other) const; void dump(XmlNode &node) const; void copyAlert(XmlNode &destination, MysqlResultRef &source, AlertHistoryType historical) const; }; protected: class SimpleExpression { private: enum Type { invalid, simpleNumber, divideByPrice, divideByVolume, multiplyByPrice, multiplyByVolume }; Type _type; double _value; public: bool isValid() const; std::string toDatabase() const; std::string toEncodedConfig() const; std::string toGUI() const; double getValue() const; SimpleExpression(); explicit SimpleExpression(std::string encodedConfig); explicit SimpleExpression(double value); }; private: BitSet _activeAlerts; std::map< unsigned, SimpleExpression > _alertQuality; // _windowFilter maps complete filter names, like "MaxPrice", to values. If // a filter is blank, it does not appear in this list. std::map< std::string, SimpleExpression > _windowFilter; BitSet _activeExchanges; SymbolLists _symbolLists; ColumnListConfig _columnListConfig; std::string _windowName; std::string _sound; ClientCookie _clientCookie; std::string alertTypeExpression() const; std::string rangeExpression(std::string dbField, std::string minName, std::string maxName) const; std::string exchangeExpression() const; std::string gapSubexpression(bool up) const; std::string gapExpression() const; std::string allFiltersExpression(UserId userId, DatabaseWithRetry &database) const; static BitSet getValidExchanges(UserId userId, DatabaseWithRetry &database); bool useHiddenSettings(UserId userId, DatabaseWithRetry &database); void loadOldFilter(PropertyList const &rawConfig, std::string const &oldName, std::string const &newName, bool negate); void loadWSFPair(std::string const &baseName, PropertyList const &rawConfig); void loadWSF(std::string const &fullName, PropertyList const &rawConfig); protected: BitSet const &getActiveAlerts() const { return _activeAlerts; } std::map< unsigned, SimpleExpression > const &getAlertQuality() const { return _alertQuality; } BitSet const &getActiveExchanges() const { return _activeExchanges; } SymbolLists const &getSymbolLists() const { return _symbolLists; } ColumnListConfig const &getColumnListConfig() const { return _columnListConfig; } std::map< std::string, SimpleExpression > const &getWindowFilters() const { return _windowFilter; } public: void clear(); void load(PropertyList const &rawConfig, UserId userId, DatabaseWithRetry &database, bool allowCustomColumns, bool allowNonFilterColumns); void load(std::string rawConfig, UserId userId, DatabaseWithRetry &database, bool allowCustomColumns, bool allowNonFilterColumns); std::string save() const; void customSql(UserId userId, DatabaseWithRetry &database, std::string selectFields, bool ascending, CustomSql &generator) const; void removeIllegalData(UserId userId, DatabaseWithRetry &database); void saveToMru(UserId userId, DatabaseWithRetry &database); // This retrieves the structure of the config window. It also retrieves the // current settings. If you call clear() first, then you just get the // structure. The old config window received both of these togather, // because it was based on the HTML/PHP code. // disabledSupported means that the client will understand the disabled // property in the list of exchanges. If the client does not support // this property, then we just remove any exchanges that the client cannot // access. // useFilterPairs is a recent addition. We now report MinPrice and MaxPrice // as one filter, not two. This simplifies a lot of things. But we have // to keep the old version to support old clients. void getForEditor(XmlNode &node, UserId userId, DatabaseWithRetry &database, bool disabledSupported = false, bool useFilterPairs = false, std::string const &language = "", bool allowSymbolListFolders = false, bool allowNegativeListIds = false) const; // This tells you which symbol lists are in use. You could get the same // information from getForEditor(), but that's *much* more complicated and it // uses the database. This method uses the standard parsing tools, but it // does not review any permissions for the shared lists, so the result might // be slightly different from getForEditor() in certain specific cases. Dave // Mabe said he needs this information for Scottrade. static void quickSymbolListInfo(std::string const &rawConfig, XmlNode &parent); // General information about alerts and filters. Similar to the *structure* // that we get for the editor. In fact, if we were starting from scratch, // we might use this function to populate the controls in the editor. This // is based heaivly on AlertConfig::getForEditor(), but only focusing on // the structure. // // Note that the config window only gets one language. The assumption is // that's a modal dialog box, so it won't change while it's being displayed. // This is stuff that might be requested only once, so we provide all // languages. // // It's not 100% clear when the client should ask for this. One idea is to // send this when the user first connects to a new alert or top list window, // like the list of columns. In that case, we could send a subset of this // data, only what's needed for that window. That way we could gaurentee // that the client is always current. // // It seems simpler just to ask for this once in a while. This is simple // to the xml file of "general information" that the client used to request // about once every 8 hours. static void generalInfo(XmlNode &node, UserId userId, DatabaseWithRetry &database); // This retrieves the settings without the structure. The new config window // seperates these two. We only need to send one copy of the structure, but // we need to send the settings for each strategy, so the user can quickly // see what's available. void getSettingsForEditor(XmlNode &node) const; // This is the metadata describing the columns. The client will need // this to display and even read the column data properly. void getInitialDescription(XmlNode &parent, PairedFilterList const &filters) const { _columnListConfig.getInitialDescription(parent, filters); _clientCookie.getInitialDescription(parent); } // This is aimed at the OddsMaker. It has the same general purpose as // getInitialDescription(). However, the goal is to format things for // a CSV file, rather than to display on screen. The CSV file will be // reasonably easy for a human or a computer to read. Column headers // will look like "Bid Size (Shares) [BS]". headers is a map from the // code used in the database query to the column header that should appear // in the config file. void getCsvNames(PropertyList &headers, PairedFilterList const &filters) const { _columnListConfig.getCsvNames(headers, filters); } std::string getWindowName() { return _windowName; } static bool hasConfigInfo(PropertyList const &rawConfig); }; // Copy the values in a format expected by the C# client. The PropertyList // maps language codes to values. As a special case, the language "" is // represented as just the base name (i.e. "units") in the XML. Other // languages, like "de", are represented like "de_units". Traditionally // english is always present as always listed as "". "" is the default if // the language you are looking for is not found. void copyLanguages(XmlNode &dest, std::string const &baseName, PropertyList const &values); // This only copies the specified language to the XML. void copyLanguageValue(XmlNode &dest, std::string const &propertyName, PropertyList const &values, std::string const &language); #endif