* Fix updating of existing file filter

* Update findFiles() to properly apply filters, sort order / direction, etc.
* Properly persist hash tags @ file entry persist
* Lots of improvements / additions to MCI for upload/download, etc. stats
* Persist processed entries @ upload (WIP, but close!)
This commit is contained in:
Bryan Ashby 2017-01-18 22:23:53 -07:00
parent e265e3cc97
commit fb176d3ab3
8 changed files with 159 additions and 66 deletions

View File

@ -46,6 +46,17 @@ module.exports = class FileBaseFilters {
return filterUuid;
}
replace(filterUuid, filterInfo) {
const filter = this.get(filterUuid);
if(!filter) {
return false;
}
filterInfo.tags = this.cleanTags(filterInfo.tags);
this.filters[filterUuid] = filterInfo;
return true;
}
remove(filterUuid) {
delete this.filters[filterUuid];
}

View File

@ -121,7 +121,13 @@ module.exports = class FileEntry {
});
},
function storeHashTags(callback) {
return callback(null);
const hashTagsArray = Array.from(self.hashTags);
async.each(hashTagsArray, (hashTag, next) => {
return FileEntry.persistHashTag(self.fileId, hashTag, next);
},
err => {
return callback(err);
});
}
],
err => {
@ -192,6 +198,30 @@ module.exports = class FileEntry {
);
}
static persistHashTag(fileId, hashTag, cb) {
fileDb.serialize( () => {
fileDb.run(
`INSERT OR IGNORE INTO hash_tag (hash_tag)
VALUES (?);`,
[ hashTag ]
);
fileDb.run(
`REPLACE INTO file_hash_tag (hash_tag_id, file_id)
VALUES (
(SELECT hash_tag_id
FROM hash_tag
WHERE hash_tag = ?),
?
);`,
[ hashTag, fileId ],
err => {
return cb(err);
}
);
});
}
loadHashTags(cb) {
fileDb.each(
`SELECT ht.hash_tag_id, ht.hash_tag
@ -226,20 +256,20 @@ module.exports = class FileEntry {
static getWellKnownMetaValues() { return Object.keys(FILE_WELL_KNOWN_META); }
static findFiles(filter, cb) {
// :TODO: build search here - return [ fileid1, fileid2, ... ]
// free form
// areaTag
// tags
// order by
// sort
filter = filter || {};
let sql =
`SELECT file_id
FROM file`;
let sql;
let sqlWhere = '';
let sqlOrderBy;
const sqlOrderDir = 'ascending' === filter.order ? 'ASC' : 'DESC';
function getOrderByWithCast(ob) {
if( [ 'dl_count', 'user_rating', 'est_release_year', 'byte_size' ].indexOf(filter.sort) > -1 ) {
return `ORDER BY CAST(${ob} AS INTEGER)`;
}
return `ORDER BY ${ob}`;
}
function appendWhereClause(clause) {
if(sqlWhere) {
@ -250,13 +280,38 @@ module.exports = class FileEntry {
sqlWhere += clause;
}
if(filter.sort) {
if(Object.keys(FILE_WELL_KNOWN_META).indexOf(filter.sort) > -1) { // sorting via a meta value?
sql =
`SELECT f.file_id
FROM file f, file_meta m`;
appendWhereClause(`f.file_id = m.file_id AND m.meta_name="${filter.sort}"`);
sqlOrderBy = `${getOrderByWithCast('m.meta_value')} ${sqlOrderDir}`;
} else {
sql =
`SELECT f.file_id, f.${filter.sort}
FROM file f`;
sqlOrderBy = getOrderByWithCast(`f.${filter.sort}`) + sqlOrderDir;
}
} else {
sql =
`SELECT f.file_id
FROM file`;
sqlOrderBy = `${getOrderByWithCast('f.file_id')} ${sqlOrderDir}`;
}
if(filter.areaTag) {
appendWhereClause(`area_tag="${filter.areaTag}"`);
appendWhereClause(`f.area_tag="${filter.areaTag}"`);
}
if(filter.terms) {
appendWhereClause(
`file_id IN (
`f.file_id IN (
SELECT rowid
FROM file_fts
WHERE file_fts MATCH "${filter.terms.replace(/"/g,'""')}"
@ -265,25 +320,24 @@ module.exports = class FileEntry {
}
if(filter.tags) {
const tags = filter.tags.split(' '); // filter stores as sep separated values
// build list of quoted tags; filter.tags comes in as a space separated values
const tags = filter.tags.split(' ').map( tag => `"${tag}"` ).join(',');
appendWhereClause(
`file_id IN (
`f.file_id IN (
SELECT file_id
FROM file_hash_tag
WHERE hash_tag_id IN (
SELECT hash_tag_id
FROM hash_tag
WHERE hash_tag IN (${tags.join(',')})
WHERE hash_tag IN (${tags})
)
)`
);
}
// :TODO: filter.orderBy
// :TODO: filter.sort
sql += `${sqlWhere} ${sqlOrderBy};`;
sql += sqlWhere + ';';
const matchingFileIds = [];
fileDb.each(sql, (err, fileId) => {
if(fileId) {

View File

@ -37,6 +37,17 @@ function setNextRandomRumor(cb) {
});
}
function getRatio(client, propA, propB) {
const a = StatLog.getUserStatNum(client.user, propA);
const b = StatLog.getUserStatNum(client.user, propB);
const ratio = ~~((a / b) * 100);
return `${ratio}%`;
}
function userStatAsString(client, statName, defaultValue) {
return (StatLog.getUserStat(client.user, statName) || defaultValue).toString();
}
function getPredefinedMCIValue(client, code) {
if(!client || !code) {
@ -69,16 +80,16 @@ function getPredefinedMCIValue(client, code) {
UN : function userName() { return client.user.username; },
UI : function userId() { return client.user.userId.toString(); },
UG : function groups() { return _.values(client.user.groups).join(', '); },
UR : function realName() { return client.user.properties.real_name; },
LO : function location() { return client.user.properties.location; },
UR : function realName() { return userStatAsString(client, 'real_name', ''); },
LO : function location() { return userStatAsString(client, 'location', ''); },
UA : function age() { return client.user.getAge().toString(); },
UB : function birthdate() { return moment(client.user.properties.birthdate).format(client.currentTheme.helpers.getDateFormat()); },
US : function sex() { return client.user.properties.sex; },
UE : function emailAddres() { return client.user.properties.email_address; },
UW : function webAddress() { return client.user.properties.web_address; },
UF : function affils() { return client.user.properties.affiliation; },
UT : function themeId() { return client.user.properties.theme_id; },
UC : function loginCount() { return client.user.properties.login_count.toString(); },
BD : function birthdate() { return moment(client.user.properties.birthdate).format(client.currentTheme.helpers.getDateFormat()); }, // iNiQUiTY
US : function sex() { return userStatAsString(client, 'sex', ''); },
UE : function emailAddres() { return userStatAsString(client, 'email_address', ''); },
UW : function webAddress() { return userStatAsString(client, 'web_address', ''); },
UF : function affils() { return userStatAsString(client, 'affiliation', ''); },
UT : function themeId() { return userStatAsString(client, 'theme_id', ''); },
UC : function loginCount() { return userStatAsString(client, 'login_count', 0); },
ND : function connectedNode() { return client.node.toString(); },
IP : function clientIpAddress() { return client.address().address; },
ST : function serverName() { return client.session.serverName; },
@ -86,26 +97,27 @@ function getPredefinedMCIValue(client, code) {
const activeFilter = FileBaseFilters.getActiveFilter(client);
return activeFilter ? activeFilter.name : '';
},
DN : function userNumDownloads() { return StatLog.getUserStat(client.user, 'dl_total_count'); }, // Obv/2
DK : function userByteDownload() { // Obv/2
const byteSize = parseInt(StatLog.getUserStat(client.user, 'dl_total_bytes')) || 0;
return formatByteSize(byteSize, true);
DN : function userNumDownloads() { return userStatAsString(client, 'dl_total_count', 0); }, // Obv/2
DK : function userByteDownload() { // Obv/2 uses DK=downloaded Kbytes
const byteSize = StatLog.getUserStatNum(client.user, 'dl_total_bytes');
return formatByteSize(byteSize, true); // true=withAbbr
},
UP : function userNumUploads() { return userStatAsString(client, 'ul_total_count', 0); }, // Obv/2
UK : function userByteUpload() { // Obv/2 uses UK=uploaded Kbytes
const byteSize = StatLog.getUserStatNum(client.user, 'ul_total_bytes');
return formatByteSize(byteSize, true); // true=withAbbr
},
NR : function userUpDownRatio() { // Obv/2
return getRatio(client, 'ul_total_count', 'dl_total_count');
},
KR : function userUpDownByteRatio() { // Obv/2 uses KR=upload/download Kbyte ratio
return getRatio(client, 'ul_total_bytes', 'dl_total_bytes');
},
// :TODO: Up/down ratio (count)
// :TODO: Up/down ratio (bytes)
MS : function accountCreated() { return moment(client.user.properties.account_created).format(client.currentTheme.helpers.getDateFormat()); },
CS : function currentStatus() { return client.currentStatus; },
PS : function userPostCount() {
const postCount = client.user.properties.post_count || 0;
return postCount.toString();
},
PC : function userPostCallRatio() {
const postCount = client.user.properties.post_count || 0;
const callCount = client.user.properties.login_count;
const ratio = ~~((postCount / callCount) * 100);
return `${ratio}%`;
},
PS : function userPostCount() { return userStatAsString(client, 'post_count', 0); },
PC : function userPostCallRatio() { return getRatio(client, 'post_count', 'login_count'); },
MD : function currentMenuDescription() {
return _.has(client, 'currentMenuModule.menuConfig.desc') ? client.currentMenuModule.menuConfig.desc : '';
@ -181,6 +193,7 @@ function getPredefinedMCIValue(client, code) {
//
// :TODO: DD - Today's # of downloads (iNiQUiTY)
//
// :TODO: System stat log for total ul/dl, total ul/dl bytes
//
// Special handling for XY

View File

@ -122,6 +122,10 @@ class StatLog {
return user.properties[statName];
}
getUserStatNum(user, statName) {
return parseInt(this.getUserStat(user, statName)) || 0;
}
incrementUserStat(user, statName, incrementBy, cb) {
incrementBy = incrementBy || 1;

View File

@ -1,7 +1,6 @@
#!/usr/bin/env node
/* jslint node: true */
'use strict';
/*

View File

@ -264,9 +264,11 @@ exports.getModule = class FileAreaFilterEdit extends MenuModule {
saveCurrentFilter(formData, cb) {
const filters = new FileBaseFilters(this.client);
const selectedFilter = this.filtersArray[this.currentFilterIndex];
if(selectedFilter) {
// *update* currently selected filter
this.setFilterValuesFromFormData(selectedFilter, formData);
filters.replace(selectedFilter.uuid, selectedFilter);
} else {
// add a new entry; note that UUID will be generated
const newFilter = {};

View File

@ -333,12 +333,13 @@ exports.getModule = class FileAreaList extends MenuModule {
if(_.isString(self.currentFileEntry.desc)) {
const descView = self.viewControllers.browse.getView(MciViewIds.browse.desc);
if(descView) {
/* :TODO: finish createCleanAnsi() and use here!!!
createCleanAnsi(
self.currentFileEntry.desc,
{ height : self.client.termHeight, width : descView.dimens.width },
cleanDesc => {
descView.setText(cleanDesc);
// :TODO: use cleanDesc -- need to finish createCleanAnsi() !!
//descView.setText(cleanDesc);
descView.setText( self.currentFileEntry.desc );
self.updateQueueIndicator();
self.populateCustomLabels('browse', 10);
@ -346,9 +347,6 @@ exports.getModule = class FileAreaList extends MenuModule {
return callback(null);
}
);
*/
descView.setText( self.currentFileEntry.desc );
}
} else {
self.updateQueueIndicator();

View File

@ -222,7 +222,10 @@ exports.getModule = class UploadModule extends MenuModule {
function prepDetails(scanResults, callback) {
async.eachSeries(scanResults.newEntries, (newEntry, nextEntry) => {
self.displayFileDetailsPageForEntry(newEntry, (err, newValues) => {
if(!err) {
if(err) {
return nextEntry(err);
}
// if the file entry did *not* have a desc, take the user desc
if(!self.fileEntryHasDetectedDesc(newEntry)) {
newEntry.desc = newValues.shortDesc.trim();
@ -235,18 +238,27 @@ exports.getModule = class UploadModule extends MenuModule {
if(newValues.tags.length > 0) {
newEntry.setHashTags(newValues.tags);
}
}
return nextEntry(err);
});
}, err => {
delete self.fileDetailsCurrentEntrySubmitCallback;
return callback(err, scanResults);
});
},
function persistNewEntries(scanResults, callback) {
// loop over entries again & persist to DB
async.eachSeries(scanResults.newEntries, (newEntry, nextEntry) => {
newEntry.persist(err => {
return nextEntry(err);
});
}, err => {
return callback(err);
});
}
],
err => {
console.log('eh'); // :TODO: remove me :)
}
);
}