Ref T203, adjustments for distribution class

* distribution is just the channel, no more the concrete file
* masterdir no longer part of distribution
* using QStringList instead of QSet in some place so sort order is kept
* new utility functions
This commit is contained in:
Klaus Basan
2017-12-09 19:38:35 +01:00
parent 340dad19a3
commit de7312a869
4 changed files with 123 additions and 301 deletions

View File

@@ -22,8 +22,8 @@ namespace BlackMisc
CDistribution::CDistribution()
{ }
CDistribution::CDistribution(const QString &channel, bool restricted) :
m_channel(channel.trimmed().toUpper()), m_restricted(restricted)
CDistribution::CDistribution(const QString &channel, int stability, bool restricted) :
m_channel(channel.trimmed().toUpper()), m_stability(stability), m_restricted(restricted)
{ }
void CDistribution::setChannel(const QString &channel)
@@ -31,99 +31,6 @@ namespace BlackMisc
m_channel = channel.trimmed().toUpper();
}
QStringList CDistribution::getPlatforms() const
{
QStringList platforms = m_platformFiles.keys();
platforms.sort();
return platforms;
}
bool CDistribution::supportsPlatform(const QString &platform) const
{
QStringList platforms = m_platformFiles.keys();
return !platform.isEmpty() && platforms.contains(platform);
}
QString CDistribution::guessMyPlatform() const
{
const QStringList platforms(getPlatforms());
if (platforms.isEmpty()) { return ""; }
QStringList reduced;
for (const QString &p : platforms)
{
if (CBuildConfig::isRunningOnWindowsNtPlatform())
{
if (!p.contains("win", Qt::CaseInsensitive)) continue;
}
else if (CBuildConfig::isRunningOnLinuxPlatform())
{
if (!p.contains("linux", Qt::CaseInsensitive)) continue;
}
else if (CBuildConfig::isRunningOnMacOSPlatform())
{
if (!(p.contains("mac", Qt::CaseInsensitive) ||
p.contains("macos", Qt::CaseInsensitive) ||
p.contains("osx", Qt::CaseInsensitive)))
{
continue;
}
}
reduced << p;
}
if (reduced.size() > 1)
{
// further reduce by VATSIM flag
QStringList furtherReduced;
for (const QString &p : as_const(reduced))
{
if (CBuildConfig::isVatsimVersion())
{
if (p.contains("vatsim")) { furtherReduced << p; }
}
else
{
if (!p.contains("vatsim")) { furtherReduced << p; }
}
}
if (!furtherReduced.isEmpty()) { reduced = furtherReduced; }
}
int wordSize = CBuildConfig::buildWordSize();
if (wordSize >= 32 && reduced.size() > 1)
{
// further reduce by word size 32/64
QStringList furtherReduced;
const QString wsString = QString::number(wordSize);
for (const QString &p : as_const(reduced))
{
if (!p.contains(wsString)) { continue; }
furtherReduced << p;
}
if (!furtherReduced.isEmpty()) { reduced = furtherReduced; }
}
if (reduced.isEmpty()) { return ""; }
return reduced.front();
}
QString CDistribution::getVersionString(const QString &platform) const
{
if (platform.isEmpty()) { return ""; }
return m_platformVersions.value(platform);
}
QVersionNumber CDistribution::getQVersion(const QString &platform) const
{
return QVersionNumber::fromString(getVersionString(platform));
}
QString CDistribution::getFilename(const QString &platform) const
{
if (platform.isEmpty()) { return ""; }
return m_platformFiles.value(platform);
}
void CDistribution::addDownloadUrl(const CUrl &url)
{
if (url.isEmpty()) { return; }
@@ -135,6 +42,21 @@ namespace BlackMisc
return !m_downloadUrls.isEmpty();
}
CIcon CDistribution::getRestrictionIcon() const
{
return CIcon::iconByIndex(m_restricted ? CIcons::StandardIconLockClosed16 : CIcons::StandardIconLockOpen16);
}
bool CDistribution::isStabilitySameOrBetter(const CDistribution &otherDistribution) const
{
return m_stability >= otherDistribution.m_stability;
}
bool CDistribution::isStabilityBetter(const CDistribution &otherDistribution) const
{
return m_stability > otherDistribution.m_stability;
}
QString CDistribution::convertToQString(bool i18n) const
{
return convertToQString(", ", i18n);
@@ -148,13 +70,15 @@ namespace BlackMisc
QLatin1String("download URLs: ") %
getDownloadUrls().toQString(i18n) %
separator %
QLatin1String("platforms: ") %
getPlatforms().join(", ") %
separator %
QLatin1String("timestamp: ") %
this->getFormattedUtcTimestampYmdhms();
}
CIcon CDistribution::toIcon() const
{
return this->getRestrictionIcon();
}
CVariant CDistribution::propertyByIndex(const BlackMisc::CPropertyIndex &index) const
{
if (index.isMyself()) { return CVariant::from(*this); }
@@ -164,10 +88,9 @@ namespace BlackMisc
switch (i)
{
case IndexChannel: return CVariant::fromValue(m_channel);
case IndexStability : return CVariant::fromValue(m_stability);
case IndexDownloadUrls: return CVariant::fromValue(m_downloadUrls);
case IndexRestricted: return CVariant::fromValue(m_restricted);
case IndexPlatforms: return CVariant::fromValue(this->getPlatforms());
case IndexPlatformFiles: return CVariant::fromValue(m_platformFiles);
default: return CValueObject::propertyByIndex(index);
}
}
@@ -185,9 +108,9 @@ namespace BlackMisc
switch (i)
{
case IndexChannel: this->setChannel(variant.value<QString>()); break;
case IndexStability: m_stability = variant.toInt(); break;
case IndexDownloadUrls: m_downloadUrls = variant.value<CUrlList>(); break;
case IndexRestricted: m_restricted = variant.toBool(); break;
case IndexPlatformFiles: m_platformFiles = variant.value<CPlatformDictionary>(); break;
default: CValueObject::setPropertyByIndex(index, variant); break;
}
}
@@ -197,8 +120,10 @@ namespace BlackMisc
Q_UNUSED(prefix); // not nested
const QString channel(json.value("channel").toString());
const bool restricted(json.value("restricted").toBool());
CDistribution distribution(channel, restricted);
const bool restricted = json.value("restricted").toBool();
const int stability = json.value("stability").toInt();
CDistribution distribution(channel, stability, restricted);
distribution.setKeyAndTimestampFromDatabaseJson(json);
// add the URLs
for (int i = 0; i < 5; i++)
@@ -208,84 +133,7 @@ namespace BlackMisc
if (url.isEmpty()) { continue; }
distribution.addDownloadUrl(CUrl(url));
}
const QJsonObject platforms = json.value("platforms").toObject();
const QStringList platformsKeys = platforms.keys();
if (platformsKeys.isEmpty()) { return CDistribution(); } // no platforms, then the whole distribution is useless
for (const QString platformKey : as_const(platformsKeys))
{
QStringList platformFileNames;
QJsonArray platformFiles = platforms.value(platformKey).toArray();
for (const QJsonValue &platformFile : platformFiles)
{
const QString filename = platformFile.toString();
if (filename.isEmpty()) { continue; }
platformFileNames << filename;
}
const QPair<QString, QVersionNumber> latestFileInfo = findLatestVersion(platformFileNames);
if (!latestFileInfo.first.isEmpty())
{
distribution.m_platformFiles.insert(platformKey, latestFileInfo.first);
distribution.m_platformVersions.insert(platformKey, latestFileInfo.second.toString());
}
}
distribution.setKeyAndTimestampFromDatabaseJson(json);
return distribution;
}
QVersionNumber CDistribution::versionNumberFromFilename(const QString &filename)
{
if (filename.isEmpty()) { return QVersionNumber(); }
// swift-installer-linux-64-0.7.3_2017-03-25_11-24-50.run
thread_local const QRegularExpression firstSegments("\\d+\\.\\d+\\.\\d+");
QRegularExpressionMatch firstSegmentsMatch = firstSegments.match(filename);
if (!firstSegmentsMatch.hasMatch())
{
return QVersionNumber(); // no version, invalid
}
QString v = firstSegmentsMatch.captured(0); // first 3 segments, like 0.9.3
if (!v.endsWith('.')) { v += '.'; }
thread_local const QRegularExpression ts1("\\d{4}.?\\d{2}.?\\d{2}.?\\d{2}.?\\d{2}.?\\d{2}");
QRegularExpressionMatch ts1Match = ts1.match(filename);
if (!ts1Match.hasMatch())
{
// version without timestamp
v += "0";
return QVersionNumber::fromString(v);
}
const QString lastSegment = BlackMisc::digitOnlyString(ts1Match.captured(0));
v += lastSegment;
return QVersionNumber::fromString(v);
}
QPair<QString, QVersionNumber> CDistribution::findLatestVersion(const QStringList &filenames)
{
if (filenames.isEmpty()) return QPair<QString, QVersionNumber>();
if (filenames.size() == 1) return QPair<QString, QVersionNumber>(filenames.first(), versionNumberFromFilename(filenames.first()));
QString latest;
QVersionNumber latestVersion;
for (const QString &fn : filenames)
{
if (latest.isEmpty())
{
latest = fn;
latestVersion = versionNumberFromFilename(fn);
continue;
}
const QVersionNumber version = versionNumberFromFilename(fn);
if (version > latestVersion)
{
latest = fn;
latestVersion = versionNumberFromFilename(fn);
}
}
return QPair<QString, QVersionNumber>(latest, latestVersion);
}
} // ns
} // ns

View File

@@ -29,30 +29,26 @@ namespace BlackMisc
{
namespace Db
{
//! Dictionary for files per platform
using CPlatformDictionary = BlackMisc::CDictionary<QString, QString>;
//! Distributions for channel
class BLACKMISC_EXPORT CDistribution :
public BlackMisc::CValueObject<CDistribution>,
public BlackMisc::Db::IDatastoreObjectWithIntegerKey
public CValueObject<CDistribution>,
public IDatastoreObjectWithIntegerKey
{
public:
//! Properties by index
enum ColumnIndex
{
IndexChannel = BlackMisc::CPropertyIndex::GlobalIndexCDistribution,
IndexChannel = CPropertyIndex::GlobalIndexCDistribution,
IndexStability,
IndexRestricted,
IndexDownloadUrls,
IndexPlatforms,
IndexPlatformFiles,
IndexDownloadUrls
};
//! Default constructor
CDistribution();
//! Constructor
CDistribution(const QString &channel, bool restricted);
CDistribution(const QString &channel, int stability, bool restricted);
//! Destructor.
~CDistribution() {}
@@ -63,29 +59,17 @@ namespace BlackMisc
//! Set the channel
void setChannel(const QString &channel);
//! Get platforms
QStringList getPlatforms() const;
//! Stability (higher is more stable)
int getStability() const { return m_stability; }
//! Supports platform?
bool supportsPlatform(const QString &platform) const;
//! Guess platform for this distribution channel and this version of swift
QString guessMyPlatform() const;
//! Version for platform
QString getVersionString(const QString &platform) const;
//! Version as QVersion
QVersionNumber getQVersion(const QString &platform) const;
//! File for platform
QString getFilename(const QString &platform) const;
//! Order
void setStability(int stability) { m_stability = stability; }
//! Download URLs, i.e. here one can download installer
const BlackMisc::Network::CUrlList &getDownloadUrls() const { return m_downloadUrls; }
const Network::CUrlList &getDownloadUrls() const { return m_downloadUrls; }
//! Add URL, ignored if empty
void addDownloadUrl(const BlackMisc::Network::CUrl &url);
void addDownloadUrl(const Network::CUrl &url);
//! At least one download URL?
bool hasDownloadUrls() const;
@@ -93,48 +77,57 @@ namespace BlackMisc
//! Restricted channel?
bool isRestricted() const { return m_restricted; }
//! Restricted channel
void setRestricted(bool r) { m_restricted = r; }
//! Get the restrict icon
CIcon getRestrictionIcon() const;
//! "this" having same or better stability than other distribution?
bool isStabilitySameOrBetter(const CDistribution &otherDistribution) const;
//! "this" having better stability than other distribution?
bool isStabilityBetter(const CDistribution &otherDistribution) const;
//! Empty?
bool isEmpty() const { return m_channel.isEmpty(); }
//! \copydoc BlackMisc::Mixin::String::toQString
QString convertToQString(bool i18n = false) const;
//! To string
QString convertToQString(const QString &separator, bool i18n = false) const;
//! Representing icon
CIcon toIcon() const;
//! \copydoc BlackMisc::Mixin::Index::propertyByIndex
BlackMisc::CVariant propertyByIndex(const BlackMisc::CPropertyIndex &index) const;
CVariant propertyByIndex(const CPropertyIndex &index) const;
//! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex
void setPropertyByIndex(const BlackMisc::CPropertyIndex &index, const BlackMisc::CVariant &variant);
void setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant);
//! Object from database JSON format
static CDistribution fromDatabaseJson(const QJsonObject &json, const QString &prefix = {});
private:
QString m_channel; //!< channel the files belong to
bool m_restricted = false; //!< restricted access (i.e. password for download needed)
BlackMisc::Network::CUrlList m_downloadUrls; //!< download URLs, here I get the installer
CPlatformDictionary m_platformFiles; //!< the latest file version per platform
CPlatformDictionary m_platformVersions; //!< the version per platform
//! Extract version number from a file name
static QVersionNumber versionNumberFromFilename(const QString &filename);
//! Find the file representing the latest version
static QPair<QString, QVersionNumber> findLatestVersion(const QStringList &filenames);
QString m_channel; //!< channel the files belong to
int m_stability; //!< stability
bool m_restricted = false; //!< restricted access (i.e. password for download needed)
Network::CUrlList m_downloadUrls; //!< download URLs, here I get the installer
BLACK_METACLASS(
CDistribution,
BLACK_METAMEMBER(dbKey),
BLACK_METAMEMBER(timestampMSecsSinceEpoch),
BLACK_METAMEMBER(channel),
BLACK_METAMEMBER(downloadUrls),
BLACK_METAMEMBER(platformFiles, 0, DisabledForComparison | DisabledForHashing),
BLACK_METAMEMBER(platformVersions, 0, DisabledForComparison | DisabledForHashing)
BLACK_METAMEMBER(stability),
BLACK_METAMEMBER(downloadUrls)
);
};
} // ns
} // ns
Q_DECLARE_METATYPE(BlackMisc::Db::CDistribution)
Q_DECLARE_METATYPE(BlackMisc::Db::CPlatformDictionary)
#endif // guard

View File

@@ -20,9 +20,9 @@ namespace BlackMisc
CSequence<CDistribution>(other)
{ }
QSet<QString> CDistributionList::getChannels() const
QStringList CDistributionList::getChannels() const
{
QSet<QString> channels;
QStringList channels;
for (const CDistribution &distribution : *this)
{
if (distribution.getChannel().isEmpty()) { continue; }
@@ -31,65 +31,55 @@ namespace BlackMisc
return channels;
}
QSet<QString> CDistributionList::findChannelsForPlatform(const QString &platform) const
void CDistributionList::sortByStability(Qt::SortOrder order)
{
QSet<QString> channels;
if (platform.isEmpty()) { return channels; }
for (const CDistribution &distribution : *this)
this->sort([order](const CDistribution & a, const CDistribution & b)
{
if (distribution.getChannel().isEmpty()) { continue; }
if (distribution.supportsPlatform(platform))
{
channels.insert(distribution.getChannel());
}
}
return channels;
const int as = a.getStability();
const int bs = b.getStability();
return order == Qt::AscendingOrder ? as < bs : bs < as;
});
}
QSet<QString> CDistributionList::getPlatforms() const
bool CDistributionList::containsEqualOrMoreStable(CDistribution &distribution) const
{
QSet<QString> platforms;
for (const CDistribution &distribution : *this)
{
if (distribution.getChannel().isEmpty()) { continue; }
platforms << distribution.getChannel();
}
return platforms;
return containsBy([&distribution](const CDistribution & dist) { return dist.isStabilitySameOrBetter(distribution); });
}
CDistribution CDistributionList::findByChannelOrDefault(const QString &channel) const
bool CDistributionList::containsUnrestricted() const
{
return this->contains(&CDistribution::isRestricted, true);
}
bool CDistributionList::containsChannel(const QString &channel) const
{
return this->contains(&CDistribution::getChannel, channel);
}
CDistribution CDistributionList::findFirstByChannelOrDefault(const QString &channel) const
{
return this->findFirstByOrDefault(&CDistribution::getChannel, channel);
}
QString CDistributionList::getVersionForChannelAndPlatform(const QString &channel, const QString &platform) const
CDistributionList CDistributionList::findByRestriction(bool restricted) const
{
const CDistribution dist = this->findByChannelOrDefault(channel);
return dist.getVersionString(platform);
return this->findBy(&CDistribution::isRestricted, restricted);
}
QVersionNumber CDistributionList::getQVersionForChannelAndPlatform(const QString &channel, const QString &platform) const
CDistribution CDistributionList::getMostStableOrDefault() const
{
const CDistribution dist = this->findByChannelOrDefault(channel);
return dist.getQVersion(platform);
if (this->size() < 2) { return this->frontOrDefault(); }
CDistributionList copy(*this);
copy.sortByStability();
return copy.back();
}
QString CDistributionList::getVersionForChannelAndPlatform(const QStringList &channelPlatform) const
CDistribution CDistributionList::getLeastStableOrDefault() const
{
Q_ASSERT_X(channelPlatform.length() != 2, Q_FUNC_INFO, "Wrong size");
return this->getVersionForChannelAndPlatform(channelPlatform.first(), channelPlatform.last());
}
QVersionNumber CDistributionList::getQVersionForChannelAndPlatform(const QStringList &channelPlatform) const
{
Q_ASSERT_X(channelPlatform.length() == 2, Q_FUNC_INFO, "Wrong size");
return this->getQVersionForChannelAndPlatform(channelPlatform.first(), channelPlatform.last());
}
QStringList CDistributionList::guessMyDefaultChannelAndPlatform() const
{
//! \fixme will be further improved when we have added a public server and have more channels
return QStringList({"ALPHA", BlackConfig::CBuildConfig::guessMyPlatformString()}); // guessing
if (this->size() < 2) { return this->frontOrDefault(); }
CDistributionList copy(*this);
copy.sortByStability();
return copy.front();
}
CDistributionList CDistributionList::fromDatabaseJson(const QJsonArray &array)

View File

@@ -14,7 +14,7 @@
#include "distribution.h"
#include "blackmisc/db/datastoreobjectlist.h"
#include "blackmisc/datacache.h"
#include "blackmisc/platform.h"
#include "blackmisc/blackmiscexport.h"
#include "blackmisc/collection.h"
#include "blackmisc/sequence.h"
@@ -27,12 +27,13 @@ namespace BlackMisc
namespace Db
{
//! Multiple distributions for different channels:
//! - one CDistribution objects contains all versions for a channel
//! - a distribution list normally contains all distributions for all channels
//! - one CDistribution objects contains all artifacts for a channel
//! - a distribution list normally contains all artifacts for all channels
//! \sa CArtifact
class BLACKMISC_EXPORT CDistributionList :
public BlackMisc::CSequence<CDistribution>,
public BlackMisc::Db::IDatastoreObjectList<CDistribution, CDistributionList, int>,
public BlackMisc::Mixin::MetaType<CDistributionList>
public CSequence<CDistribution>,
public IDatastoreObjectList<CDistribution, CDistributionList, int>,
public Mixin::MetaType<CDistributionList>
{
public:
BLACKMISC_DECLARE_USING_MIXIN_METATYPE(CDistributionList)
@@ -44,31 +45,31 @@ namespace BlackMisc
CDistributionList(const CSequence<CDistribution> &other);
//! All channels
QSet<QString> getChannels() const;
QStringList getChannels() const;
//! Find channels for platform
QSet<QString> findChannelsForPlatform(const QString &platform) const;
//! Stability
void sortByStability(Qt::SortOrder order = Qt::AscendingOrder);
//! All platforms for all channels
QSet<QString> getPlatforms() const;
//! Contains distributions considered of same stability or more stable
bool containsEqualOrMoreStable(CDistribution &distribution) const;
//! Find distribution by channels
CDistribution findByChannelOrDefault(const QString &channel) const;
//! Contains any unrestricted
bool containsUnrestricted() const;
//! Version for specific channel and platform
QString getVersionForChannelAndPlatform(const QString &channel, const QString &platform) const;
//! Contains channel?
bool containsChannel(const QString &channel) const;
//! Version for specific channel and platform
QVersionNumber getQVersionForChannelAndPlatform(const QString &channel, const QString &platform) const;
//! Find by channel
CDistribution findFirstByChannelOrDefault(const QString &channel) const;
//! Version for specific channel and platform
QString getVersionForChannelAndPlatform(const QStringList &channelPlatform) const;
//! Find by restriction flag
CDistributionList findByRestriction(bool restricted) const;
//! Version for specific channel and platform
QVersionNumber getQVersionForChannelAndPlatform(const QStringList &channelPlatform) const;
//! Most stable or default
CDistribution getMostStableOrDefault() const;
//! Guess the best channel/platform
QStringList guessMyDefaultChannelAndPlatform() const;
//! Least stable or default
CDistribution getLeastStableOrDefault() const;
//! From database JSON by array
static CDistributionList fromDatabaseJson(const QJsonArray &array);
@@ -76,16 +77,6 @@ namespace BlackMisc
//! From database JSON by string
static CDistributionList fromDatabaseJson(const QString &json);
};
//! Trait for distributions
struct TDistributionsInfo : public BlackMisc::TDataTrait<CDistributionList>
{
//! Key in data cache
static const char *key() { return "distributions"; }
//! First load is synchronous
static constexpr bool isPinned() { return true; }
};
} // ns
} // ns