ulsparser
Version:
Helper library for parsing ULS database files (from http://wireless.fcc.gov/uls/index.htm?job=transaction&page=weekly)
391 lines (357 loc) • 11.1 kB
JavaScript
var lineReader = require('line-reader'),
path = require('path'),
logCodeMap = require("./hs_codes");
var classMap = {
E: "Amateur Extra",
A: "Advanced",
G: "General",
P: "Technician Plus",
T: "Technician",
N: "Novice"
};
var entityTypeMap = {
L: "Licensee",
CL: "Licensee Contact",
O: "Owner",
R: "Assignor",
E: "Transferee",
CR: "Assignor Contact",
CE: "Transferee Contact",
S: "Lessee",
CS: "Lesee Contact"
};
var applicantTypeCodeMap = {
B: "Amateur Club",
C: "Corporation",
D: "General Partnership",
E: "Limited Partnership",
F: "Limited Liability Partnership",
G: "Governmental Entity",
H: "Other",
I: "Individual",
J: "Joint Venture",
L: "Limited Liability Company",
M: "Military Recreation",
O: "Consortium",
P: "Partnership",
R: "RACES",
T: "Trust",
U: "Unincorporated Association"
};
var licenseStatusMap = {
A: "Active",
C: "Canceled",
E: "Expired",
L: "Pending Legal Status",
P: "Parent Station Canceled",
T: "Terminated"
};
var attachmentCodeMap = {
A: "Application",
B: "Cellular Cross Interest",
C: "Confidentiality",
D: "Divestiture",
E: "603-T (Spectrum Leasing)",
F: "Fee Exemption",
G: "BRSChannel 1,2,2A Notification",
I: "Indirect Ownership",
L: "Letter",
M: "800 MHz Band Reconfiguration",
N: "Ownership",
O: "Other",
P: "Pleading",
Q: "Confidential Pleading",
R: "Data Correction",
S: "1.2112(a)(6)",
T: "47 C. F. R. 17.14 ASR Exemption",
U: "Quiet Zone Consent",
W: "Waiver",
X: "Tribal Govt. Certification",
Y: "Tribal Lands Waiver Request",
Z: "TCNS Internal Reply",
NBAND: "Rule 90.209(b)(6) Certification"
};
var map = {
AM: function(doc) { // Amateur
return {
type: doc[0],
u_id: doc[1],
uls_filenumber: doc[2],
ebf_number: doc[3],
callsign: doc[4],
license_class: classMap[doc[5]] || doc[5],
group_code: doc[6], // Unsure what this is
region_code: doc[7],
trustee_callsign: doc[8],
is_trustee: doc[9],
has_physician_cert: doc[10], // Unsure what this is
has_ve_signature: doc[11], // Unsure what this is
call_change_req: doc[12],
is_vanity_change: doc[13], // Unsure what this is
vanity_relationship: doc[14], // Unsure what this is
prev_callsign: doc[15],
prev_license_class: classMap[doc[16]] || doc[16],
trustee_name: doc[17]
};
},
CO: function(doc) { // Comments
return {
type: doc[0],
u_id: doc[1],
uls_filenumber: doc[2],
callsign: doc[3],
comment_date: doc[4],
comment_text: doc[5],
status_code: doc[6],
status_date: doc[7]
};
},
EN: function(doc) { // Entity
var ret = {
type: doc[0],
u_id: doc[1],
uls_filenumber: doc[2],
ebf_number: doc[3],
callsign: doc[4],
entity_type: entityTypeMap[doc[5]] || doc[5],
licensee_id: doc[6],
entity_name: doc[7],
first_name: doc[8],
middle_initial: doc[9],
last_name: doc[10],
suffix: doc[11],
phone: doc[12],
fax: doc[13],
email: doc[14],
address: doc[15],
city: doc[16],
state: doc[17],
zip: doc[18],
pobox: doc[19],
attn_line: doc[20],
sgin: doc[21],
frn: doc[22],
applicant_type: applicantTypeCodeMap[doc[23]] || doc[23],
status_code: doc[25],
status_date: doc[26]
};
if (ret.applicant_type == "Other") { ret.applicant_type = doc[24]; }
return ret;
},
HD: function(doc) { // Application / License Header
return {
type: doc[0],
u_id: doc[1],
uls_filenumber: doc[2],
ebf_number: doc[3],
callsign: doc[4],
license_status: licenseStatusMap[doc[5]] || doc[5],
radio_service_code: doc[6],
grant_date: doc[7],
expired_date: doc[8],
cancellation_date: doc[9],
eligibility_rule_num: doc[10],
is_alien: doc[12],
is_alien_government: doc[13],
is_alien_corp: doc[14],
is_alien_officer: doc[15],
is_alien_control: doc[16],
is_revoked: doc[17],
is_convicted: doc[18],
is_adjudged: doc[19],
is_common_carrier: doc[21],
is_noncommon_carrier: doc[22],
is_private_comm: doc[23],
is_fixed: doc[24],
is_mobile: doc[25],
is_radiolocation: doc[26],
is_satellite: doc[27],
is_dev_or_demo: doc[28],
is_interconnected_service: doc[29],
certifier_first_name: doc[30],
certifier_middle_initial: doc[31],
certifier_last_name: doc[32],
certifier_suffix: doc[33],
certifier_title: doc[34],
// I seriously doubt these are ever filled out, but in the interest of supporting the spec...
is_female: doc[35],
is_black: doc[36], // I'm not trying to be racist here, but is_black_or_african_american would be ridiculous to type
is_native_american: doc[37],
is_hawaiian: doc[38],
is_asian: doc[39],
is_white: doc[40],
is_hispanic: doc[41],
effective_date: doc[42],
last_action_date: doc[43],
auction_id: doc[44],
broadcast_services_reg_status: doc[45],
band_manager_reg_status: doc[46],
broadcast_services_type_of_radio: doc[47],
alien_ruling: doc[48],
is_licensee_name_change: doc[49]
};
},
HS: function(doc) { // History
return {
type: doc[0],
u_id: doc[1],
uls_filenumber: doc[2],
callsign: doc[3],
log_date: doc[4],
code: doc[5],
code_text: logCodeMap[doc[5]] || doc[5]
};
},
LA: function(doc) { // License Attachments; note that these aren't real useful since we can't get the files
return {
type: doc[0],
u_id: doc[1],
callsign: doc[2],
attachment_code: doc[3],
attachment_type: attachmentCodeMap[doc[3]] || doc[3],
attachment_desc: doc[4],
attachment_date: doc[5],
attachment_filename: doc[6],
action_performed: doc[7]
};
},
SC: function(doc) { // Special Condition
return {
type: doc[0],
u_id: doc[1],
uls_filenumber: doc[2],
ebf_number: doc[3],
callsign: doc[4],
special_condition_type: doc[5], // Can't find any docs for this
special_condition_code: doc[6], // Can't find any docs for this
status_code: doc[7], // Can't find any docs for this
status_date: doc[8]
};
},
SF: function(doc) { // License Free Form Special Condition
// ... whatever that is supposed to mean?
return {
type: doc[0],
u_id: doc[1],
uls_filenumber: doc[2],
ebf_number: doc[3],
callsign: doc[4],
license_free_form_type: doc[5],
unique_license_free_form_id: doc[6],
sequence_number: doc[7],
license_free_form_condition: doc[8],
status_code: doc[9],
status_date: doc[10]
};
}
};
function ULSParser(dirPath) {
this.paths = {
root: dirPath,
AM: path.join(dirPath, "AM.dat"),
CO: path.join(dirPath, "CO.dat"),
EN: path.join(dirPath, "EN.dat"),
HD: path.join(dirPath, "HD.dat"),
HS: path.join(dirPath, "HS.dat"),
LA: path.join(dirPath, "LA.dat"),
SC: path.join(dirPath, "SC.dat"),
SF: path.join(dirPath, "SF.dat"),
all: path.join(dirPath, "sorted.psv")
};
this.rootPath = dirPath;
return this;
}
var p = ULSParser.prototype;
p.parseEntriesForType = function(t, processLine) {
var self = this;
var path = this.paths[t];
lineReader.eachLine(path, function(line, last, cb) {
line = line.trim().split("|");
var type = line[0];
var doc;
// First see if we have a translator for it
if (map[type]) {
doc = map[type](line);
} else {
doc = line;
}
var doneCb = function(stop) {
if (stop) {
cb(false);
} else {
if (last) {
processLine(null);
} else {
cb();
}
}
};
processLine(doc, doneCb);
});
};
/**
* handlers expects an object formatted like so:
*
* [
* ["EN", function(line, cb) { ... }],
* ["HD", function(line, cb) { ... }],
* ...
* ]
*
* One function per type
*
*/
p.parseSortedFile = function(filename, handlers, oncomplete) {
var path = path.join(this.rootPath, filename);
function getBlankObj() {
return {
AM: [], CO: [], EN: [], HD: [], HS: [], LA: [], SC: [], SF: []
};
}
var curGroup = null;
var cur_uid = null;
function processGroup(group, nextCb) {
if (!group) { return; } // handle null case
var q = [];
handlers.forEach(function(v) {
curGroup[v[0]].forEach(function(l) {
q.push([v[1], l]);
});
});
curGroup = getBlankObj();
// Now that we have a "q" of all functions to run for this user,
// execute them asynchronously
function next() {
if (!q.length) {
// done with this q
return nextCb();
}
var cmd = p.shift();
cmd[0](cmd[1], next);
}
next();
}
lineReader.eachLine(path, function(line, last, cb) {
line = line.trim().split("|");
function cont(nextCb) {
curGroup[line[0]].push(line);
if (last) {
processGroup(curGroup, oncomplete);
}
}
if (cur_uid != line[1].trim()) {
processGroup(curGroup, function() {
cont(cb);
});
} else {
cont(cb);
}
});
};
module.exports = ULSParser;
ULSParser.classMap = classMap;
ULSParser.entityTypeMap = entityTypeMap;
ULSParser.applicantTypeCodeMap = applicantTypeCodeMap;
ULSParser.licenseStatusMap = licenseStatusMap;
ULSParser.attachmentCodeMap = attachmentCodeMap;
ULSParser.logCodeMap = logCodeMap;