Source identity
| Field | Recorded value |
|---|---|
| Source type | GitHub source |
| Original record | Janady13/oversize-route-builder / server/utils/permitParser.js |
| Date | Not specified in source |
| Repository | Janady13/oversize-route-builder |
| Revision | 8630196cd162b68cf069cdabcc5487b49dcc625d |
| Branch | main |
| Path or archive member | server/utils/permitParser.js |
| Original SHA-256 | f4ec276f24d835ccd6c027b7ec23c46672d82066ccc62987c1661bd6dd28c374 |
| Git blob SHA | f7e7cea18fec7ae9e5194185c8476934c4ef0efe |
| Reading-copy SHA-256 | f4ec276f24d835ccd6c027b7ec23c46672d82066ccc62987c1661bd6dd28c374 |
Recovered source transcript
Download complete source text ↓262 lines · 8.1 KB
/**
* Parse permit data from PDF text
* Supports Oklahoma and Texas permits (expandable to other states)
*/
const parsePermitData = (text, state) => {
const result = {
permitNumber: null,
permitType: null,
issuedDate: null,
startDate: null,
endDate: null,
loadSpecs: {},
route: {},
vehicle: {},
restrictions: []
};
try {
if (state === 'OK' || state === 'OKLAHOMA') {
return parseOklahomaPermit(text);
} else if (state === 'TX' || state === 'TEXAS') {
return parseTexasPermit(text);
} else {
return parseGenericPermit(text);
}
} catch (error) {
console.error('Permit parsing error:', error);
return result;
}
};
const parseOklahomaPermit = (text) => {
const result = {
permitNumber: extractPattern(text, /Permit Number:\s*(\d+)/i),
permitType: 'single-trip',
issuedDate: extractDate(text, /Issued:\s*(\d{2}\/\d{2}\/\d{4})/i),
startDate: extractDate(text, /Start Date:\s*(\d{2}\/\d{2}\/\d{4})/i),
endDate: extractDate(text, /End Date:\s*(\d{2}\/\d{2}\/\d{4})/i),
loadSpecs: {},
route: {},
vehicle: {},
restrictions: []
};
// Extract load specifications
result.loadSpecs = {
maxWidth: extractDimension(text, /Max Width:\s*([\d-]+)/i),
maxHeight: extractDimension(text, /Max Height:\s*([\d-]+)/i),
maxLength: extractDimension(text, /Total Length:\s*([\d-]+)/i),
grossWeight: parseInt(extractPattern(text, /Gross Weight:\s*(\d+)/i) || '0'),
overweightBy: parseInt(extractPattern(text, /Overweight By:\s*(\d+)/i) || '0'),
numAxles: parseInt(extractPattern(text, /Number of Axles:\s*(\d+)/i) || '0'),
description: extractPattern(text, /Load Description:\s*([^\n]+)/i)
};
// Extract vehicle information
result.vehicle = {
truckMake: extractPattern(text, /Truck Make:\s*([^\s]+)/i),
truckYear: parseInt(extractPattern(text, /Truck Year:\s*(\d{4})/i) || '0'),
tagNumber: extractPattern(text, /Tag Number:\s*([^\s]+)/i),
tagState: extractPattern(text, /Tag State:\s*([A-Z]{2})/i),
trailerMake: extractPattern(text, /Trailer Make:\s*([^\n]+?)\s*Trailer Year/i),
trailerYear: parseInt(extractPattern(text, /Trailer Year:\s*(\d{4})/i) || '0')
};
// Extract route information
const originMatch = text.match(/Starting From:\s*([^,\n]+)/i);
const destMatch = text.match(/Going To:\s*([^,\n]+)/i);
result.route = {
origin: originMatch ? originMatch[1].trim() : null,
destination: destMatch ? destMatch[1].trim() : null,
totalDistance: parseFloat(extractPattern(text, /Approximate Mileage:\s*([\d.]+)/i) || '0'),
estimatedTime: null,
directions: extractOklahomaDirections(text),
restrictions: extractOklahomaRestrictions(text)
};
return result;
};
const parseTexasPermit = (text) => {
const result = {
permitNumber: extractPattern(text, /Permit Number:\s*(\d+)/i),
permitType: 'single-trip',
issuedDate: extractDate(text, /Issued On:\s*(\d{2}\/\d{2}\/\d{4})/i),
startDate: extractDate(text, /Effective:\s*(\d{2}\/\d{2}\/\d{4})/i),
endDate: extractDate(text, /Expiration:\s*(\d{2}\/\d{2}\/\d{4})/i),
loadSpecs: {},
route: {},
vehicle: {},
restrictions: []
};
// Extract load specifications
result.loadSpecs = {
maxWidth: extractDimension(text, /Max\.\s*Width:\s*([\d']+)/i),
maxHeight: extractDimension(text, /Max\.\s*Height:\s*([\d'\s]+)/i),
maxLength: extractDimension(text, /Max\.\s*Length:\s*([\d']+)/i),
grossWeight: parseInt(extractPattern(text, /Gross Weight:\s*(\d+)/i) || '0'),
overweightBy: 0,
numAxles: parseInt(extractPattern(text, /Axle:\s*(\d+)/i) || '0'),
description: extractPattern(text, /Load Description:\s*([^\n]+)/i)
};
// Extract vehicle information
result.vehicle = {
truckMake: extractPattern(text, /Truck:\s*(\d{4})\s*([A-Z]+)/i, 2),
truckYear: parseInt(extractPattern(text, /Truck:\s*(\d{4})/i) || '0'),
tagNumber: extractPattern(text, /Tag Number:\s*([^\s]+)/i),
tagState: extractPattern(text, /Tag State:\s*([A-Z]{2})/i),
trailerMake: extractPattern(text, /Trailer:\s*([^\n]+)/i)
};
// Extract route information
const originMatch = text.match(/Origin:\s*([^\n]+)/i);
const destMatch = text.match(/Destination:\s*([^\n]+)/i);
result.route = {
origin: originMatch ? originMatch[1].trim() : null,
destination: destMatch ? destMatch[1].trim() : null,
totalDistance: parseFloat(extractPattern(text, /(\d+\.?\d*)\s*mi/i) || '0'),
estimatedTime: null,
directions: extractTexasDirections(text),
restrictions: extractTexasRestrictions(text)
};
return result;
};
const parseGenericPermit = (text) => {
// Generic parser for unknown states
const result = {
permitNumber: extractPattern(text, /permit\s*#?:?\s*([A-Z0-9-]+)/i) ||
extractPattern(text, /number:?\s*([A-Z0-9-]+)/i),
permitType: 'single-trip',
issuedDate: extractDate(text, /issued:?\s*(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4})/i),
startDate: extractDate(text, /start:?\s*(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4})/i),
endDate: extractDate(text, /end:?\s*(\d{1,2}[\/\-]\d{1,2}[\/\-]\d{2,4})/i),
loadSpecs: {
maxWidth: extractDimension(text, /width:?\s*([\d'\-\s]+)/i),
maxHeight: extractDimension(text, /height:?\s*([\d'\-\s]+)/i),
maxLength: extractDimension(text, /length:?\s*([\d'\-\s]+)/i),
grossWeight: parseInt(extractPattern(text, /weight:?\s*(\d+)/i) || '0')
},
route: {},
vehicle: {},
restrictions: []
};
return result;
};
// Helper functions
const extractPattern = (text, pattern, group = 1) => {
const match = text.match(pattern);
return match ? match[group].trim() : null;
};
const extractDate = (text, pattern) => {
const dateStr = extractPattern(text, pattern);
if (!dateStr) return null;
try {
const date = new Date(dateStr);
return date.toISOString().split('T')[0];
} catch {
return null;
}
};
const extractDimension = (text, pattern) => {
const dim = extractPattern(text, pattern);
if (!dim) return null;
// Convert formats like "16-0" or "16' 0\"" to decimal
const parts = dim.split(/[-'\s]/);
if (parts.length >= 2) {
return parseFloat(parts[0]) + (parseFloat(parts[1]) || 0) / 12;
}
return parseFloat(dim) || null;
};
const extractOklahomaDirections = (text) => {
const directions = [];
const directionPattern = /(\d+\.?\d*)\s*mi\s+([^\n]+)/gi;
let match;
while ((match = directionPattern.exec(text)) !== null) {
directions.push({
distance: parseFloat(match[1]),
instruction: match[2].trim()
});
}
return directions;
};
const extractTexasDirections = (text) => {
const directions = [];
// Look for the route table
const routeSection = text.match(/Miles\s+Route\s+To\s+Distance.*?Final Destination/s);
if (routeSection) {
const lines = routeSection[0].split('\n');
lines.forEach(line => {
const match = line.match(/^([\d.]+)\s+(.+?)\s+([\d.]+)\s+([\d:]+)$/);
if (match) {
directions.push({
distance: parseFloat(match[1]),
route: match[2].trim(),
instruction: '',
cumulativeDistance: parseFloat(match[3]),
estimatedTime: match[4]
});
}
});
}
return directions;
};
const extractOklahomaRestrictions = (text) => {
const restrictions = [];
// Common restriction patterns
const patterns = [
/no.*movement.*\d+:\d+.*\d+:\d+/gi,
/escort.*required/gi,
/contact.*utility/gi,
/daylight.*only/gi,
/travel.*prohibited/gi
];
patterns.forEach(pattern => {
const matches = text.match(pattern);
if (matches) {
matches.forEach(m => restrictions.push(m.trim()));
}
});
return [...new Set(restrictions)]; // Remove duplicates
};
const extractTexasRestrictions = (text) => {
const restrictions = [];
// Extract from General Conditions section
const conditionsMatch = text.match(/General Conditions:?(.*?)(?=Origin:|$)/s);
if (conditionsMatch) {
const conditions = conditionsMatch[1].split(/\d+\s+/).filter(c => c.trim());
restrictions.push(...conditions.map(c => c.trim().substring(0, 200))); // Limit length
}
return restrictions;
};
module.exports = {
parsePermitData
};