#ifndef __TopListConfig_h_ #define __TopListConfig_h_ #include #include "../shared/MarketHours.h" #include "AlertConfig.h" #include "WorkerThread.h" // Ideally AlertConfig would use this. However, that's unlikely because it // has too much baggage. I didn't want to copy too much of that code here // for that reason. We should be able to make things simpler by starting // fresh. class FilterListConfig { private: // If a filter is not set, the key will be missing from the map. Note that // we got rid of the SimpleExpression class. That should be obsolete because // of the idea of custom per user filters, i.e. the user_filters table. std::map< std::string, double > _min, _max; void getFromConfigWindow(PropertyList const &config, std::string baseName, std::string prefix, std::map< std::string, double > &m); void getValuesForEditor(XmlNode &parent, std::string prefix, std::map< std::string, double > const &m) const; public: void clear(); std::string asSaveConfig() const; void getFromConfigWindow(PropertyList const &config, PairedFilterList const &filters); std::string allFiltersExpression(PairedFilterList const &filters) const; static void getStructureForEditor(XmlNode &parent, PairedFilterList const &filters, std::string const &language); void getValuesForEditor(XmlNode &parent) const; std::map< std::string, double > const &getMin() const { return _min; } std::map< std::string, double > const &getMax() const { return _max; } }; // This is specific to the top list. For consistency I'm making it into its // own class, like FilterListConfig. It's good to seperate this into pieces, // but maybe some of these should be private. class SortByConfig { private: std::string _field; bool _highestOnTop; std::string asString() const; public: SortByConfig() { clear(); } void clear(); std::string asSaveConfig() const; void getFromConfigWindow(PropertyList const &config, PairedFilterList const &filters); std::string orderBySql(PairedFilterList const &filters) const; void getValuesForEditor(XmlNode &parent) const; void getInitialDescription(XmlNode &parent) const; std::string field() const { return _field; } bool highestOnTop() const { return _highestOnTop; } }; // We always made this simpler than in an alert window. In particular, we // don't have two different encodings for this data. class SimpleExchangesConfig { private: BitSet _activeExchanges; static BitSet getValidExchanges(UserId userId, DatabaseWithRetry &database); public: void clear(); std::string asSaveConfig() const; void getFromConfigWindow(PropertyList const &config); std::string getSql() const; void getValuesForEditor(XmlNode &parent) const; static void getStructureForEditor(XmlNode &parent, UserId userId, DatabaseWithRetry &database); void removeIllegalData(UserId userId, DatabaseWithRetry &database); BitSet const &getActiveExchanges() const { return _activeExchanges; } }; class TopListTimeConfig { private: bool _history; // _outsideMarketHours is only meaningful is _history is false. If // _outsideMarketHours is true, we show pre and post market data. Otherwise // we freeze the output at the close. bool _outsideMarketHours; // _time is only meaningful is _history is true. time_t _time; public: TopListTimeConfig() { clear(); } void clear() { _history = false; _outsideMarketHours = false; _time = 0; } std::string asSaveConfig() const; void getValuesForEditor(XmlNode &parent, bool easternTime) const; void getFromConfigWindow(PropertyList const &config); void quickLoad(std::string const &config); static void getStructureForEditor(XmlNode &parent, DatabaseWithRetry &database); time_t getValue() const { return _time; } bool history() const { return _history; } bool outsideMarketHours() const { return _outsideMarketHours; } }; class TopListConfig { private: // 0 means off. Otherwise, this is in microseconds. If a query // takes longer than this, report it to the log. static TimeVal::Microseconds logTime; ColumnListConfig _columnList; FilterListConfig _filterList; SymbolLists _symbolLists; SortByConfig _sortBy; SimpleExchangesConfig _exchanges; TopListTimeConfig _time; std::string _windowName; static const int DEFAULT_COUNT = 100; int _count; ClientCookie _clientCookie; enum Mode { tlmDelayed, // Small delay: Grab from the alerts table. tlmLive, // No delay: Grab from the top list table. tlmHistory // Possibly grab from the archive tables. }; struct Location { Mode mode; bool endOfDay; bool mostRecentClose; // Invariant: endTime does not need mysqlEscapeString. Ideally it would // always be a valid mysql datetime. But the biger concern is avoiding // a syntax error. Sending the empty string (for example) would at most // cause a warning. std::string endTime; // userStartTime and userEndTime are reported to the user. They are // formatted the way the client likes to receive times. std::string userStartTime, userEndTime; AlertId minId; AlertId maxId; std::string tableName; // If we can't find the max value, use something really small. If we can't // find the min value, use the largest integer. If we try to search // between those two values, it will fail immediately. The alternative is // to scan the entire table. // (This is a fail safe. These values should never actually be used. A // couple of times I've seen a strategy using a min of 0 and a reasonably // max. That shouldn't happen. When it did, it made the database very // slow. If that bug appears again, we'll very quickly return an empty // list to the user. I never found the bug.) static int64_t invalidMax() { return std::numeric_limits< int64_t >::min(); } static int64_t invalidMin() { return std::numeric_limits< int64_t >::max(); } Location() : mode(tlmDelayed), endOfDay(false), mostRecentClose(false), minId(invalidMin()), maxId(invalidMax()) { } bool valid() const; TclList debugDump() const; }; void findData(Location &location, UserId userId, bool useEasternTime, DatabaseWithRetry &database, bool forceDelayMode) const; protected: ColumnListConfig const &getColumnList() const { return _columnList; } FilterListConfig const &getFilterList() const { return _filterList; } SymbolLists const &getSymbolLists() const { return _symbolLists; } SortByConfig const &getSortBy() const { return _sortBy; } SimpleExchangesConfig const &getExchanges() const { return _exchanges; } TopListTimeConfig const &getTime() const { return _time; } int getCount() const { return _count; } public: TopListConfig() : _count(DEFAULT_COUNT) { } void clear(); void load(PropertyList const &rawConfig, UserId userId, DatabaseWithRetry &database, bool allowNonFilterColumns); void load(std::string rawConfig, UserId userId, DatabaseWithRetry &database, bool allowNonFilterColumns); std::string save() const; void removeIllegalData(UserId userId, DatabaseWithRetry &database); void saveToMru(UserId userId, DatabaseWithRetry &database); void getInitialDescription(XmlNode &node, UserId userId, DatabaseWithRetry &database) const; static void getStructureForEditor(XmlNode &node, UserId userId, bool allowSymbolListFolders, bool allowNegativeListIds, DatabaseWithRetry &database, std::string const &language); void getSettingsForEditor(XmlNode &node, bool easternTime) const; std::string getWindowName() const { return _windowName; } DayNumber dayNumber(DatabaseWithRetry &database) const; bool history() const { return _time.history(); } typedef std::vector< PropertyList > Rows; struct AllData { Rows rows; std::string startTime; std::string endTime; }; void getData(AllData &allData, UserId userId, bool useEasternTime, DatabaseWithRetry &database, bool showAllRows = false) const; ColumnListConfig::Mappings const &getColumnMappings() const { return _columnList.getMappings(); } static void globalInit(); }; #endif