Purpose
Find NAPC solicitation reports matching a caller's work type, location, institution, project name, keyword, and optional posting-date range, then return complete details from each solicitation, including report number, title, agency, dates, scope, contacts, documents, and priority information.
When to Use
Use for archived or current NAPC solicitation searches when the caller supplies a keyword, locality, institution, state, work type, date range, or priority item. The site requires an authenticated session before report pages can be used.
Workflow
- Establish an authenticated NAPC session at
https://www.napc.pro/login/using caller-provided credentials. Submitinput#username,input#userpass, andinput[name='loginbtn']; never store credentials in the skill or URLs. - Navigate directly to the filtered report URL. Base form:
https://www.napc.pro/nsi_reports.php?formstatus=all&archive=1&formscope={keyword}&formstateprovid%5B%5D={state-code}&formworktype%5B%5D={work-type}&formage={start-date}&formbidto={end-date}&o=2&d=1&p=0Omit unrequested filters. URL-encode{keyword}and date values.archive=1includes archived reports,formstatus=allincludes all statuses,o=2&d=1applies the observed date ordering, andp=0selects the first result page. For a named project, university, campus, or locality, put the search phrase informscoperather than guessing a report identifier. Run additional searches with alternative distinctive terms when needed, then deduplicate results. - For demolition/abatement searches, use
formworktype%5B%5D=DEM. The observed Texas state option isformstateprovid%5B%5D=44. Additional observed work-type values areASB,HZW,LPA,MLD, andWDM; use them only when requested or when the caller wants the broader related-work set. Multiple work types may be supplied by repeating the parameter, for exampleformworktype%5B%5D=DEM&formworktype%5B%5D=ASB. - On each result page, run the list extractor below. Collect every
reports3.php?formprjid=...URL and visible row metadata. Follow pagination by incrementingp(p=1,p=2, etc.) until a page has no report links or no new records. Deduplicate by list URL or report number. Keep requested date ranges as a client-side check because date formatting and matching may vary. - Navigate directly to each collected opaque-ID report URL and run the detail extractor below. Merge list metadata with detail fields, preserve the canonical source URL, and return all matching records. Do not stop after finding one priority or keyword match.
List-page extractor (evaluate on each nsi_reports.php results page):
(() => {
const clean = (v) => (v || "").replace(/\\s+/g, " ").trim();
const abs = (a) => new URL(a.getAttribute("href"), location.href).href;
const seen = new Set();
return [...document.querySelectorAll('a[href*="reports3.php?formprjid="]')]
.map((a) => {
const url = abs(a);
if (seen.has(url)) return null;
seen.add(url);
const row = a.closest("tr");
const cells = row
? [...row.querySelectorAll("th,td")].map((x) => clean(x.innerText))
: [];
return {
title: clean(a.innerText),
url,
bidDate: cells[0] || "",
posted: cells[2] || "",
workType: cells[4] || "",
city: cells[5] || "",
state: cells[6] || "",
status: cells[7] || "",
reportNum: cells[8] || "",
rowText: clean(row?.innerText || a.innerText),
};
})
.filter(Boolean);
})();Detail-page extractor (evaluate on each reports3.php report page):
(() => {
const clean = (v) => (v || "").replace(/\\s+/g, " ").trim();
const result = {
url: location.href,
title: clean(
document.querySelector('h1,h2,.title,[class*="title"]')?.textContent,
),
fields: {},
sections: [],
documents: [],
};
for (const row of document.querySelectorAll("tr")) {
const cells = [...row.querySelectorAll("th,td")]
.map((x) => clean(x.textContent))
.filter(Boolean);
if (cells.length >= 2) result.fields[cells[0]] = cells.slice(1).join(" | ");
}
for (const dt of document.querySelectorAll("dt")) {
const key = clean(dt.textContent),
dd = dt.nextElementSibling;
if (key && dd) result.fields[key] = clean(dd.textContent);
}
for (const block of document.querySelectorAll(
'section,article,.panel,.card,[class*="section"],[class*="detail"]',
)) {
const text = clean(block.innerText);
if (text && text.length > 20) result.sections.push(text);
}
for (const a of document.querySelectorAll("a[href]")) {
const text = clean(a.textContent);
const href = new URL(a.getAttribute("href"), location.href).href;
if (
text &&
/download|document|attachment|spec|plan|pdf|file/i.test(text + " " + href)
)
result.documents.push({ text, href });
}
result.bodyText = clean(
document.querySelector("main")?.innerText || document.body.innerText,
);
return result;
})();Site-Specific Gotchas
- Authentication is session-based; direct report URLs work only after login. Never place credentials in URLs, recipes, or extracted output.
- The report search form submits to
nsi_reports.php; multi-select parameters require bracket notation, encoded asformstateprovid%5B%5Dandformworktype%5B%5D. formscopeis the direct URL keyword filter and works for project names, institutions, campuses, cities, and other distinctive terms. Search terms such asSSC,Stephenville, andTarleton Statecan be tried separately when a combined phrase is too restrictive.archive=1andformstatus=allare needed when the request includes older or closed solicitations.o=2&d=1is the observed ordering used for date-oriented results.- Pagination is controlled by the
pquery parameter. Fetch every page and deduplicate because keyword and category searches can overlap. DEMis the observed option value for Demolition. Treat abatement as a requested keyword or inspect returned scope/title rather than assuming it has a separate work-type code.ASBcan be combined withDEMby repeating the encoded multi-select parameter when both categories are requested.- Report links contain opaque
formprjididentifiers. Resolve them from result-page anchors and use the returned href; never invent an ID from a title or project name. - Result tables contain mixed navigation and records. Restrict the list extractor to
a[href*="reports3.php?formprjid="]and use the containing table row for the column mapping; review any candidate whose title or row text is not clearly a solicitation. - Date query values should use the format accepted by the live NAPC form; always verify returned posting and bid dates against the requested range client-side.
Expected Output
Return one record per matching solicitation with report number or identifier, title, agency/owner, location, posting and bid dates, work type, scope/description, priority status, contacts, all relevant detail fields, document/attachment URLs, and canonical source URL. Include pagination coverage, searches or filters used, and identify requested keywords or priority items found.