diff --git a/core/file_base_filter.js b/core/file_base_filter.js index 10628747..b6598670 100644 --- a/core/file_base_filter.js +++ b/core/file_base_filter.js @@ -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]; } diff --git a/core/file_entry.js b/core/file_entry.js index 42712541..b93ea967 100644 --- a/core/file_entry.js +++ b/core/file_entry.js @@ -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) { diff --git a/core/predefined_mci.js b/core/predefined_mci.js index 9ad300e7..b0f4178b 100644 --- a/core/predefined_mci.js +++ b/core/predefined_mci.js @@ -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 diff --git a/core/stat_log.js b/core/stat_log.js index 4d53581a..41c4ceb5 100644 --- a/core/stat_log.js +++ b/core/stat_log.js @@ -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; diff --git a/main.js b/main.js index ab8d2652..ef624294 100755 --- a/main.js +++ b/main.js @@ -1,7 +1,6 @@ #!/usr/bin/env node /* jslint node: true */ - 'use strict'; /* diff --git a/mods/file_area_filter_edit.js b/mods/file_area_filter_edit.js index e7935f9a..3b848e91 100644 --- a/mods/file_area_filter_edit.js +++ b/mods/file_area_filter_edit.js @@ -262,11 +262,13 @@ exports.getModule = class FileAreaFilterEdit extends MenuModule { } saveCurrentFilter(formData, cb) { - const filters = new FileBaseFilters(this.client); - const selectedFilter = this.filtersArray[this.currentFilterIndex]; + 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 = {}; diff --git a/mods/file_area_list.js b/mods/file_area_list.js index 33adc8ba..d1b9c351 100644 --- a/mods/file_area_list.js +++ b/mods/file_area_list.js @@ -333,22 +333,20 @@ 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); return callback(null); } - ); - */ - - descView.setText( self.currentFileEntry.desc ); + ); } } else { self.updateQueueIndicator(); diff --git a/mods/upload.js b/mods/upload.js index 63c99961..d01aa827 100644 --- a/mods/upload.js +++ b/mods/upload.js @@ -222,31 +222,43 @@ exports.getModule = class UploadModule extends MenuModule { function prepDetails(scanResults, callback) { async.eachSeries(scanResults.newEntries, (newEntry, nextEntry) => { self.displayFileDetailsPageForEntry(newEntry, (err, newValues) => { - if(!err) { - // if the file entry did *not* have a desc, take the user desc - if(!self.fileEntryHasDetectedDesc(newEntry)) { - newEntry.desc = newValues.shortDesc.trim(); - } + if(err) { + return nextEntry(err); + } - if(newValues.estYear.length > 0) { - newEntry.meta.est_release_year = newValues.estYear; - } + // if the file entry did *not* have a desc, take the user desc + if(!self.fileEntryHasDetectedDesc(newEntry)) { + newEntry.desc = newValues.shortDesc.trim(); + } - if(newValues.tags.length > 0) { - newEntry.setHashTags(newValues.tags); - } + if(newValues.estYear.length > 0) { + newEntry.meta.est_release_year = newValues.estYear; + } + + 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 :) } ); }