Omaha #3685 Add support for sending specific products in mixed case

Change-Id: I3c7e8bfe285ce7344ddfccf28173a68f3396b126

Former-commit-id: 24989f60b0 [formerly 64162dd9c0] [formerly e729cd8814] [formerly 24989f60b0 [formerly 64162dd9c0] [formerly e729cd8814] [formerly 5d07d13aa0 [formerly e729cd8814 [formerly 18d4fc13ebbd77b3a476bc70252fae1322d631c6]]]]
Former-commit-id: 5d07d13aa0
Former-commit-id: b4f0bcf30c [formerly ba737603f1] [formerly b19a1bfd8fdde387cbe24e24036130761b12c523 [formerly 5e711922d7]]
Former-commit-id: 11e1b6d8e8ec4c312af84389b87680ad8e5d98d0 [formerly 3f248e341a]
Former-commit-id: 94c875ced4
This commit is contained in:
Ron Anderson 2014-10-22 13:32:18 -05:00
parent 0fbb9afab6
commit 779a5f59eb
27 changed files with 8017 additions and 7922 deletions

1
.gitignore vendored
View file

@ -7,6 +7,7 @@ testBin/
bin-test/
*.class
*.pyo
*.pyc
*.o
*.orig

View file

@ -33,6 +33,7 @@
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# 02/12/2014 #2591 randerso Added retry when loading combinations fails
# 10/20/2014 #3685 randerso Changed default of lowerCase to True if not specified
import string, getopt, sys, time, os, types, math
import ModuleAccessor
@ -191,7 +192,7 @@ class TextFormatter:
if language is not None:
text = product.translateForecast(text, language)
# Convert to Upper Case
if not forecastDef.get('lowerCase', 0):
if not forecastDef.get('lowerCase', True):
text = text.upper()
else:
text = "Text Product Type Invalid " + \

View file

@ -27,6 +27,12 @@
#
# Author: hansen
# ----------------------------------------------------------------------------
#
# SOFTWARE HISTORY
#
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# 10/20/2014 #3685 randerso Changes to support mixed case products
import EditAreaUtils
import StringUtils
@ -49,7 +55,7 @@ class Header(EditAreaUtils.EditAreaUtils, StringUtils.StringUtils):
cityDescriptor ="Including the cities of",
areaList=None, includeCities=1, includeZoneNames=1,
includeIssueTime=1, includeCodes=1, includeVTECString=1,
hVTECString=None, accurateCities=False):
hVTECString=None, accurateCities=False, upperCase=True):
# Make a UGC area header for the given areaLabel
# Determine list of areas (there could be more than one if we are using a combination)
@ -227,7 +233,17 @@ class Header(EditAreaUtils.EditAreaUtils, StringUtils.StringUtils):
if cityString != "":
numCities = len(string.split(cityString, "...")[1:])
if numCities == 1:
cityDescriptor = string.replace(cityDescriptor, "CITIES", "CITY")
def preserveCase(matchobj):
orig = matchobj.group(0)
repl = 'city'
retv = ''
for i in range(len(repl)):
c = repl[i]
if orig[i].isupper():
c = c.upper()
retv = retv + c
return retv
cityDescriptor = re.sub("cities", preserveCase, cityDescriptor, flags=re.IGNORECASE)
cityString = self.endline(cityDescriptor + cityString,
linelength=self._lineLength, breakStr=["..."])
issueTimeStr = issueTimeStr + "\n\n"
@ -249,6 +265,8 @@ class Header(EditAreaUtils.EditAreaUtils, StringUtils.StringUtils):
if includeVTECString == 0:
VTECString = ""
header = codeString + VTECString + nameString + cityString + issueTimeStr
if upperCase:
header = header.upper()
return header
# Make accurate city list based on the grids
@ -569,8 +587,8 @@ class Header(EditAreaUtils.EditAreaUtils, StringUtils.StringUtils):
if entry.has_key("fullStateName"):
state = entry["fullStateName"]
#Special District of Columbia case
if state == "DISTRICT OF COLUMBIA":
state = "THE DISTRICT OF COLUMBIA"
if state.upper() == "DISTRICT OF COLUMBIA":
state = "The District of Columbia"
# Get part-of-state information
partOfState = ""
if entry.has_key("partOfState"):
@ -583,15 +601,15 @@ class Header(EditAreaUtils.EditAreaUtils, StringUtils.StringUtils):
if entry.has_key("ugcCode"):
codeType = entry["ugcCode"][2]
if codeType == "Z":
nameType = "ZONE"
nameType = "zone"
elif codeType == "C":
indCty=entry.get("independentCity", 0)
if indCty == 1:
nameType = "INDEPENDENT CITY"
elif state == "LOUISIANA":
nameType = "PARISH"
nameType = "independent city"
elif state == "Louisiana":
nameType = "parish"
else:
nameType = "COUNTY"
nameType = "county"
else:
codeType == "?"
value = (state, partOfState)

View file

@ -95,6 +95,7 @@ import com.raytheon.uf.common.activetable.VTECChange;
import com.raytheon.uf.common.activetable.VTECTableChangeNotification;
import com.raytheon.uf.common.dataplugin.gfe.textproduct.DraftProduct;
import com.raytheon.uf.common.dataplugin.gfe.textproduct.ProductDefinition;
import com.raytheon.uf.common.dataplugin.text.db.MixedCaseProductSupport;
import com.raytheon.uf.common.jms.notification.INotificationObserver;
import com.raytheon.uf.common.jms.notification.NotificationException;
import com.raytheon.uf.common.jms.notification.NotificationMessage;
@ -158,6 +159,7 @@ import com.raytheon.viz.ui.dialogs.ICloseCallback;
* 02/05/2014 17022 ryu Modified loadDraft() to fix merging of WMO heading and AWIPS ID.
* 03/25/2014 #2884 randerso Added xxxid to check for disabling editor
* 05/12/2014 16195 zhao Modified widgetSelected() for "Auto Wrap" option widget
* 10/20/2014 #3685 randerso Made conversion to upper case conditional on product id
*
* </pre>
*
@ -480,7 +482,7 @@ public class ProductEditorComp extends Composite implements
break;
case SWT.Show:
if ((!dead)
&& (getProductText() != null || !getProductText()
&& ((getProductText() != null) || !getProductText()
.isEmpty())) {
timeUpdater.schedule();
}
@ -707,7 +709,7 @@ public class ProductEditorComp extends Composite implements
Rectangle trim = p.computeTrim(0, 0, 0, 0);
Point dpi = p.getDPI();
int leftMargin = dpi.x + trim.x;
int topMargin = dpi.y / 2 + trim.y;
int topMargin = (dpi.y / 2) + trim.y;
GC gc = new GC(p);
Font font = gc.getFont();
String printText = textComp.getProductText();
@ -866,8 +868,8 @@ public class ProductEditorComp extends Composite implements
autoWrapMI.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent e) {
wrapMode = !wrapMode;
textComp.setAutoWrapMode(wrapMode);
wrapMode = !wrapMode;
textComp.setAutoWrapMode(wrapMode);
}
});
@ -1159,7 +1161,9 @@ public class ProductEditorComp extends Composite implements
textComp.getTextEditorST().setText(
textComp.getTextEditorST().getText() + "\n");
}
textComp.upper();
if (!MixedCaseProductSupport.isMixedCase(getNNNid())) {
textComp.upper();
}
textComp.endUpdate();
if (!frameCheck(false)) {
@ -1292,7 +1296,7 @@ public class ProductEditorComp extends Composite implements
"Error sending active table request to http server ", e);
}
if (records == null || records.isEmpty()) {
if ((records == null) || records.isEmpty()) {
activeVtecRecords = null;
} else {
if (pil != null) {
@ -1391,7 +1395,7 @@ public class ProductEditorComp extends Composite implements
activeRecs = getMatchingActiveVTEC(zones, oid, phen, sig,
etn);
String eventStr = "." + phen + "." + sig + "." + etn;
if (activeRecs == null || activeRecs.isEmpty()) {
if ((activeRecs == null) || activeRecs.isEmpty()) {
statusHandler.handle(Priority.PROBLEM,
"No active records found for " + vtec);
} else {
@ -1406,7 +1410,7 @@ public class ProductEditorComp extends Composite implements
// segment invalid due to the event going into
// effect in part of the segment area
if (started > 0 && started < activeRecs.size()) {
if ((started > 0) && (started < activeRecs.size())) {
final String msg = "Event "
+ eventStr
+ " has gone into effect in part"
@ -1508,8 +1512,8 @@ public class ProductEditorComp extends Composite implements
}
// Check start and ending time for end later than start
if (vtecStart != null && vtecEnd != null
&& vtecStart.getTime() >= vtecEnd.getTime()) {
if ((vtecStart != null) && (vtecEnd != null)
&& (vtecStart.getTime() >= vtecEnd.getTime())) {
setTabColorFunc(productStateEnum.New);
String msg = "VTEC ending time is before "
+ "starting time. Product is invalid and must"
@ -1520,13 +1524,13 @@ public class ProductEditorComp extends Composite implements
// Give 30 minutes of slack to a couple of action codes
// check the ending time and transmission time
if ((action.equals("EXP") || action.equals("CAN"))
&& vtecEnd != null) {
vtecEnd.setTime(vtecEnd.getTime() + 30
* TimeUtil.MILLIS_PER_MINUTE);
&& (vtecEnd != null)) {
vtecEnd.setTime(vtecEnd.getTime()
+ (30 * TimeUtil.MILLIS_PER_MINUTE));
}
if (vtecEnd != null
&& vtecEnd.getTime() <= transmissionTime.getTime()) {
if ((vtecEnd != null)
&& (vtecEnd.getTime() <= transmissionTime.getTime())) {
setTabColorFunc(productStateEnum.New);
String msg = "VTEC ends before current time."
+ " Product is invalid and must be regenerated.";
@ -1596,7 +1600,7 @@ public class ProductEditorComp extends Composite implements
// time contains, if time range (tr) contains time (t), return 1 def
public boolean contains(Date t) {
return t.getTime() >= startTime.getTime() && t.before(endTime);
return (t.getTime() >= startTime.getTime()) && t.before(endTime);
}
public Date getStartTime() {
@ -1650,7 +1654,7 @@ public class ProductEditorComp extends Composite implements
zones = decodeUGCs(segData);
vtecs = getVTEClines(segData);
newVtecs = fixVTEC(zones, vtecs, transmissionTime);
if (newVtecs != null && !newVtecs.isEmpty()) {
if ((newVtecs != null) && !newVtecs.isEmpty()) {
textComp.replaceText(tipVtec, newVtecs);
}
} catch (VizException e) {
@ -1761,7 +1765,9 @@ public class ProductEditorComp extends Composite implements
textComp.startUpdate();
try {
textComp.upper();
if (!MixedCaseProductSupport.isMixedCase(getNNNid())) {
textComp.upper();
}
status1 = frameCheck(true);
boolean status2 = changeTimes();
if (status1 && status2) {
@ -1935,7 +1941,8 @@ public class ProductEditorComp extends Composite implements
int sel = hoursSpnr.getSelection();
int hours = sel / 100;
int minuteInc = (sel % 100) / 25;
int purgeOffset = hours * TimeUtil.MINUTES_PER_HOUR + minuteInc * 15; // minutes
int purgeOffset = (hours * TimeUtil.MINUTES_PER_HOUR)
+ (minuteInc * 15); // minutes
Date now = SimulatedTime.getSystemTime().getTime();
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
@ -1943,7 +1950,7 @@ public class ProductEditorComp extends Composite implements
cal.add(Calendar.MINUTE, purgeOffset);
int min = cal.get(Calendar.MINUTE);
if ((min % 15) >= 1) {
cal.set(Calendar.MINUTE, (min / 15 + 1) * 15);
cal.set(Calendar.MINUTE, ((min / 15) + 1) * 15);
cal.set(Calendar.SECOND, 0);
}
this.expireDate = cal.getTime();
@ -2130,7 +2137,7 @@ public class ProductEditorComp extends Composite implements
long delta = expireTimeSec % roundSec;
long baseTime = (expireTimeSec / roundSec) * roundSec
* TimeUtil.MILLIS_PER_SECOND;
if (delta / TimeUtil.SECONDS_PER_MINUTE >= 1) {
if ((delta / TimeUtil.SECONDS_PER_MINUTE) >= 1) {
expireTime.setTime(baseTime
+ (roundSec * TimeUtil.MILLIS_PER_SECOND));
} else { // within 1 minute, don't add next increment
@ -2288,7 +2295,8 @@ public class ProductEditorComp extends Composite implements
} catch (IOException e) {
MessageBox mb = new MessageBox(parent.getShell(), SWT.OK
| SWT.ICON_WARNING);
mb.setText("Formatter AutoWrite failed: " + this.pil);
mb.setText("Error");
mb.setMessage("Formatter AutoWrite failed: " + this.pil);
mb.open();
}
}
@ -2422,7 +2430,7 @@ public class ProductEditorComp extends Composite implements
private void displayCallToActionsDialog(int callToActionType) {
// Allow only one of the 3 types of dialogs to be displayed.
if (ctaDialog != null && ctaDialog.getShell() != null
if ((ctaDialog != null) && (ctaDialog.getShell() != null)
&& !ctaDialog.isDisposed()) {
ctaDialog.bringToTop();
return;
@ -2673,6 +2681,10 @@ public class ProductEditorComp extends Composite implements
return productId;
}
public String getNNNid() {
return textdbPil.substring(3, 6);
}
public String getProductName() {
return productName;
}
@ -2901,24 +2913,24 @@ public class ProductEditorComp extends Composite implements
// Word-wrap the whole selection.
int curLine = styledText.getLineAtOffset(selectionRange.x);
int lastSelIdx = selectionRange.x + selectionRange.y - 1;
int lastSelIdx = (selectionRange.x + selectionRange.y) - 1;
int lastLine = styledText.getLineAtOffset(lastSelIdx);
int[] indices = null;
while (curLine <= lastLine && curLine < styledText.getLineCount()) {
while ((curLine <= lastLine) && (curLine < styledText.getLineCount())) {
int lineOff = styledText.getOffsetAtLine(curLine);
// word wrap a block, and find out how the text length changed.
indices = textComp.wordWrap(styledText, lineOff, wrapColumn);
int firstIdx = indices[0];
int lastIdx = indices[1];
int newLen = indices[2];
int oldLen = 1 + lastIdx - firstIdx;
int oldLen = (1 + lastIdx) - firstIdx;
int diff = newLen - oldLen;
// adjust our endpoint for the change in length
lastSelIdx += diff;
lastLine = styledText.getLineAtOffset(lastSelIdx);
// newLen doesn't include \n, so it can be 0. Don't allow
// firstIdx+newLen-1 to be < firstIdx, or loop becomes infinite.
int lastWrapIdx = Math.max(firstIdx, firstIdx + newLen - 1);
int lastWrapIdx = Math.max(firstIdx, (firstIdx + newLen) - 1);
// move down to the next unwrapped line
curLine = styledText.getLineAtOffset(lastWrapIdx) + 1;
}
@ -2979,7 +2991,7 @@ public class ProductEditorComp extends Composite implements
String str = null;
Object obj = productDefinition.get(key);
if (obj != null && obj instanceof Collection) {
if ((obj != null) && (obj instanceof Collection)) {
Collection<?> collection = (Collection<?>) obj;
str = (String) (collection.toArray())[0];
} else {

View file

@ -115,6 +115,7 @@ import org.eclipse.ui.menus.IMenuService;
import com.raytheon.uf.common.activetable.SendPracticeProductRequest;
import com.raytheon.uf.common.dataplugin.text.RemoteRetrievalResponse;
import com.raytheon.uf.common.dataplugin.text.alarms.AlarmAlertProduct;
import com.raytheon.uf.common.dataplugin.text.db.MixedCaseProductSupport;
import com.raytheon.uf.common.dataplugin.text.db.OperationalStdTextProduct;
import com.raytheon.uf.common.dataplugin.text.db.PracticeStdTextProduct;
import com.raytheon.uf.common.dataplugin.text.db.StdTextProduct;
@ -341,6 +342,7 @@ import com.raytheon.viz.ui.dialogs.SWTMessageBox;
* 13May2014 2536 bclement moved WMO Header to common, switched from TimeTools to TimeUtil
* 11Sep2014 3580 mapeters Replaced SerializationTuil usage with JAXBManager,
* removed IQueryTransport usage (no longer exists).
* 20Oct2014 3685 randerso Made conversion to upper case conditional on product id
*
* </pre>
*
@ -1096,10 +1098,9 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
* Search and replace dialog.
*/
private SearchReplaceDlg searchReplaceDlg;
/**
* Flag indicating if the overwrite mode has been set for
* template editing.
/**
* Flag indicating if the overwrite mode has been set for template editing.
*/
private boolean isTemplateOverwriteModeSet = false;
@ -2065,7 +2066,7 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
overStrikeItem.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
if (!AFOSParser.isTemplate) {
if (!AFOSParser.isTemplate) {
if (overwriteMode == true) {
overwriteMode = false;
editorInsertCmb.select(INSERT_TEXT);
@ -3714,14 +3715,14 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
editorInsertCmb.addSelectionListener(new SelectionAdapter() {
@Override
public void widgetSelected(SelectionEvent event) {
if (!AFOSParser.isTemplate) {
if (editorInsertCmb.getSelectionIndex() == INSERT_TEXT
&& overwriteMode == true) {
if (!AFOSParser.isTemplate) {
if ((editorInsertCmb.getSelectionIndex() == INSERT_TEXT)
&& (overwriteMode == true)) {
textEditor.invokeAction(ST.TOGGLE_OVERWRITE);
overwriteMode = false;
overStrikeItem.setSelection(false);
} else if (editorInsertCmb.getSelectionIndex() == OVERWRITE_TEXT
&& overwriteMode == false) {
} else if ((editorInsertCmb.getSelectionIndex() == OVERWRITE_TEXT)
&& (overwriteMode == false)) {
textEditor.invokeAction(ST.TOGGLE_OVERWRITE);
overwriteMode = true;
overStrikeItem.setSelection(true);
@ -3887,7 +3888,7 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
event.doit = false; // Ignore Ctrl+Shift+PageDown
} else if (event.keyCode == SWT.INSERT) {
// Ins key on the keypad
if (AFOSParser.isTemplate) {
if (AFOSParser.isTemplate) {
if (overwriteMode == true) {
overwriteMode = false;
overStrikeItem.setSelection(false);
@ -3917,43 +3918,43 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
if (event.keyCode == SWT.BS) {
event.doit = false;
int currentPos = textEditor.getCaretOffset();
String textUpToCaret = textEditor.getText().substring(0, currentPos);
int leftMost=textUpToCaret.lastIndexOf("[") + 1;
int rightMost = textEditor.getText().indexOf("]",currentPos);
String textUpToCaret = textEditor.getText().substring(
0, currentPos);
int leftMost = textUpToCaret.lastIndexOf("[") + 1;
int rightMost = textEditor.getText().indexOf("]",
currentPos);
int editableTextWidth = rightMost - leftMost;
String leftPart="";
String rightPart="";
String leftPart = "";
String rightPart = "";
if (currentPos == leftMost) {
leftPart = "";
rightPart = textEditor.getText().substring(
currentPos, rightMost);
textEditor.setCaretOffset(leftMost);
}
else if (currentPos > leftMost && currentPos <= rightMost){
leftPart = textEditor.getText().substring(
leftMost, currentPos - 1);
leftPart = "";
rightPart = textEditor.getText().substring(
currentPos, rightMost);
}
else if (currentPos == rightMost) {
leftPart = textEditor.getText().substring(
leftMost, currentPos-1);
textEditor.setCaretOffset(leftMost);
} else if ((currentPos > leftMost)
&& (currentPos <= rightMost)) {
leftPart = textEditor.getText().substring(leftMost,
currentPos - 1);
rightPart = textEditor.getText().substring(
currentPos, rightMost);
} else if (currentPos == rightMost) {
leftPart = textEditor.getText().substring(leftMost,
currentPos - 1);
rightPart = "";
}
String newString = leftPart + rightPart;
int neededPadSpaces = editableTextWidth - newString.length();
int neededPadSpaces = editableTextWidth
- newString.length();
String newPaddedString = String.format("%1$-"
+ (neededPadSpaces+1) + "s", newString);
+ (neededPadSpaces + 1) + "s", newString);
String spacedoutString = String.format("%1$-"
+ (editableTextWidth) + "s",
" ");
+ (editableTextWidth) + "s", " ");
textEditor.replaceTextRange(leftMost,
spacedoutString.length(), spacedoutString);
textEditor.replaceTextRange(leftMost,
newPaddedString.length(), newPaddedString);
textEditor.setCaretOffset(currentPos - 1);
} else if (event.keyCode == SWT.TAB) {
if (!isTemplateOverwriteModeSet) {
if (overwriteMode) {
@ -3968,11 +3969,11 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
String textUpToCaret = textEditor.getText().substring(
0, currentPos);
int openBracketPos = textUpToCaret.lastIndexOf("[");
openBracketPos = textEditor.getText().indexOf("[", currentPos);
openBracketPos = textEditor.getText().indexOf("[",
currentPos);
textEditor.setCaretOffset(openBracketPos + 1);
}
else if (event.keyCode>=97 && event.keyCode <=122 ||
event.keyCode>=48 && event.keyCode <=57){
} else if (((event.keyCode >= 97) && (event.keyCode <= 122))
|| ((event.keyCode >= 48) && (event.keyCode <= 57))) {
event.doit = true;
}
}
@ -4160,7 +4161,7 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
* Enter the text editor mode.
*/
private void enterEditor() {
initTemplateOverwriteMode();
initTemplateOverwriteMode();
StdTextProduct product = TextDisplayModel.getInstance()
.getStdTextProduct(token);
if ((product != null)
@ -4401,7 +4402,8 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
.getProductCategory(token)
+ tdm.getProductDesignator(token);
// Set the header text field.
if (bbbid.equals("NOR") || (bbbid.isEmpty() && tdm.getAfosPil(token) != null)) {
if (bbbid.equals("NOR")
|| (bbbid.isEmpty() && (tdm.getAfosPil(token) != null))) {
String wmoId = tdm.getWmoId(token);
wmoId = (wmoId.length() > 0 ? wmoId : "-");
String siteId = tdm.getSiteId(token);
@ -4902,7 +4904,8 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
if (warnGenFlag) {
QCConfirmationMsg qcMsg = new QCConfirmationMsg();
if (!qcMsg.checkWarningInfo(headerTF.getText().toUpperCase(),
textEditor.getText().toUpperCase(), prod.getNnnid())) {
MixedCaseProductSupport.conditionalToUpper(prod.getNnnid(),
textEditor.getText()), prod.getNnnid())) {
WarnGenConfirmationDlg wgcd = new WarnGenConfirmationDlg(shell,
qcMsg.getTitle(), qcMsg.getProductMessage(),
qcMsg.getModeMessage());
@ -4986,7 +4989,8 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
StdTextProduct prod = getStdTextProduct();
EmergencyConfirmationMsg emergencyMsg = new EmergencyConfirmationMsg();
if (emergencyMsg.checkWarningInfo(headerTF.getText().toUpperCase(),
textEditor.getText().toUpperCase(), prod.getNnnid()) == false) {
MixedCaseProductSupport.conditionalToUpper(prod.getNnnid(),
textEditor.getText()), prod.getNnnid()) == false) {
WarnGenConfirmationDlg wgcd = new WarnGenConfirmationDlg(shell,
emergencyMsg.getTitle(), emergencyMsg.getProductMessage(),
@ -5016,8 +5020,9 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
*/
private void warngenCloseCallback(boolean resend) {
// DR14553 (make upper case in product)
String body = textEditor.getText().toUpperCase();
StdTextProduct prod = getStdTextProduct();
String body = MixedCaseProductSupport.conditionalToUpper(
prod.getNnnid(), textEditor.getText());
CAVEMode mode = CAVEMode.getMode();
boolean isOperational = (CAVEMode.OPERATIONAL.equals(mode) || CAVEMode.TEST
.equals(mode));
@ -5031,7 +5036,6 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
inEditMode = false;
}
if (!resend) {
StdTextProduct prod = getStdTextProduct();
OUPTestRequest testReq = new OUPTestRequest();
testReq.setOupRequest(createOUPRequest(prod,
prod.getProduct()));
@ -5075,8 +5079,6 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
String product = TextDisplayModel.getInstance().getProduct(
token);
// TODO: Should not need to call getProduct and the like twice.
StdTextProduct prod = getStdTextProduct();
OUPRequest req = createOUPRequest(prod, product);
@ -5093,8 +5095,10 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
} else {
try {
if (!resend) {
body = VtecUtil.getVtec(removeSoftReturns(textEditor
.getText()));
body = VtecUtil
.getVtec(removeSoftReturns(MixedCaseProductSupport
.conditionalToUpper(prod.getNnnid(),
textEditor.getText())));
}
updateTextEditor(body);
if ((inEditMode || resend)
@ -5168,7 +5172,7 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
private static String copyEtn(String from, String to) {
VtecObject fromVtec = VtecUtil.parseMessage(from);
if (fromVtec != null && "NEW".equals(fromVtec.getAction())) {
if ((fromVtec != null) && "NEW".equals(fromVtec.getAction())) {
VtecObject toVtec = VtecUtil.parseMessage(to);
if (toVtec != null) {
toVtec.setSequence(fromVtec.getSequence());
@ -5204,8 +5208,8 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
body.append("\n");
}
body.append(textEditor.getText().trim());
if (AFOSParser.isTemplate){
if (AFOSParser.isTemplate) {
return removePreformat(body.toString());
}
@ -5279,7 +5283,9 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
String header = headerTF.getText().toUpperCase();
String body = resend ? resendMessage()
: removeSoftReturns(textEditor.getText().toUpperCase());
: removeSoftReturns(MixedCaseProductSupport
.conditionalToUpper(product.getNnnid(),
textEditor.getText()));
// verify text
headerTF.setText(header);
updateTextEditor(body);
@ -5302,10 +5308,11 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
if (!isAutoSave) {
if (!resend) {
// If not a resend, set the DDHHMM field to the current time
if (productText.startsWith("- -") && productText.contains("DDHHMM")) {
if (productText.startsWith("- -")
&& productText.contains("DDHHMM")) {
productText = getUnofficeProduct(currentDate);
} else {
productText = replaceDDHHMM(productText, currentDate);
productText = replaceDDHHMM(productText, currentDate);
}
VtecObject vtecObj = VtecUtil.parseMessage(productText);
if (warnGenFlag) {
@ -5341,7 +5348,7 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
productText += ATTACHMENT_STR
+ statusBarLabel.getText().substring(startIndex);
}
if (AFOSParser.isTemplate) {
productText = removePreformat(productText);
}
@ -5909,14 +5916,15 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
if (m.find()) {
SimpleDateFormat headerFormat = new SimpleDateFormat(
"hmm a z EEE MMM d yyyy");
TimeZone tz = TextWarningConstants.timeZoneShortNameMap
.get(m.group(5));
TimeZone tz = TextWarningConstants.timeZoneShortNameMap.get(m
.group(5));
if (tz != null) {
headerFormat.setTimeZone(tz);
product = product.replace(m.group(1), headerFormat.format(now)
.toUpperCase());
} else {
statusHandler.warn("Could not sync MND header time because the time zone could not be determined. Will proceed with save/send.");
statusHandler
.warn("Could not sync MND header time because the time zone could not be determined. Will proceed with save/send.");
}
}
return product;
@ -6944,9 +6952,9 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
}
String textProduct = product.getASCIIProduct();
if ((product.getNnnid() + product.getXxxid())
.startsWith(AFOSParser.DRAFT_PIL) ||
(product.getNnnid() + product.getXxxid())
.startsWith(AFOSParser.MCP_NNN )) {
.startsWith(AFOSParser.DRAFT_PIL)
|| (product.getNnnid() + product.getXxxid())
.startsWith(AFOSParser.MCP_NNN)) {
String[] nnnxxx = TextDisplayModel.getNnnXxx(textProduct);
String operationalPil = nnnxxx[0] + nnnxxx[1];
String siteNode = SiteAbbreviationUtil.getSiteNode(nnnxxx[1]);
@ -8576,7 +8584,7 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
return bbb;
}
}
private void initTemplateOverwriteMode() {
if (AFOSParser.isTemplate) {
editorInsertCmb.setEnabled(false);
@ -8594,8 +8602,7 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
isTemplateOverwriteModeSet = true;
}
}
else {
} else {
editorInsertCmb.setEnabled(true);
overStrikeItem.setEnabled(true);
editorCutBtn.setEnabled(true);
@ -8603,26 +8610,26 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
editorPasteBtn.setEnabled(true);
editorFillBtn.setEnabled(true);
editorAttachBtn.setEnabled(true);
if (isTemplateOverwriteModeSet && !overwriteMode){
if (isTemplateOverwriteModeSet && !overwriteMode) {
textEditor.invokeAction(ST.TOGGLE_OVERWRITE);
isTemplateOverwriteModeSet=false;
isTemplateOverwriteModeSet = false;
}
if (!isTemplateOverwriteModeSet && overwriteMode){
if (!isTemplateOverwriteModeSet && overwriteMode) {
textEditor.invokeAction(ST.TOGGLE_OVERWRITE);
}
}
}
private String removePreformat(String preformattedText) {
String modifiedText = preformattedText.replaceAll("\\[|\\]", " ");
modifiedText = removeSoftReturns(modifiedText);
return modifiedText;
}
private String getUnofficeProduct(String currDate)
{
StdTextProduct textProd = TextDisplayModel.getInstance().getStdTextProduct(token);
private String getUnofficeProduct(String currDate) {
StdTextProduct textProd = TextDisplayModel.getInstance()
.getStdTextProduct(token);
String header = headerTF.getText();
String nnn = textProd.getNnnid();
@ -8630,21 +8637,23 @@ public class TextEditorDialog extends CaveSWTDialog implements VerifyListener,
String nnnXxx = nnn + xxx;
String site = SiteMap.getInstance().getSite4LetterId(
textProd.getCccid());
String wmoId = textProd.getCccid() + nnnXxx + " "
+ getAddressee() + "\nTTAA00 " + site;
String wmoId = textProd.getCccid() + nnnXxx + " " + getAddressee()
+ "\nTTAA00 " + site;
header = header.replaceFirst("\n" + nnnXxx, "");
header = header.replaceFirst("-", "ZCZC");
header = header.replaceFirst("-", wmoId);
if (currDate != null)
if (currDate != null) {
header = header.replaceFirst("DDHHMM", currDate);
else
} else {
header = header.replaceFirst("DDHHMM", textProd.getHdrtime());
String body = textEditor.getText().toUpperCase();
}
header = header + "\n\n"+body +"\n!--not sent--!";
String body = MixedCaseProductSupport.conditionalToUpper(nnn,
textEditor.getText());
header = header + "\n\n" + body + "\n!--not sent--!";
return header;
}

View file

@ -47,6 +47,13 @@
value="textws/gui"
recursive="true">
</path>
<path
application="TextWS"
localizationType="COMMON_STATIC"
name="Mixed Case"
value="mixedCase"
recursive="false">
</path>
</extension>
</plugin>

View file

@ -106,6 +106,7 @@ import com.vividsolutions.jts.simplify.TopologyPreservingSimplifier;
* Sep 30, 2013 #2361 njensen Use JAXBManager for XML
* Jan 21, 2014 #2720 randerso Improve efficiency of merging polygons in edit area generation
* Aug 27, 2014 #3563 randerso Fix issue where edit areas are regenerated unnecessarily
* Oct 20, 2014 #3685 randerso Changed structure of editAreaAttrs to keep zones from different maps separated
*
* </pre>
*
@ -131,7 +132,7 @@ public class MapManager {
private final Map<String, List<String>> editAreaMap = new HashMap<String, List<String>>();
private final Map<String, Map<String, Object>> editAreaAttrs = new HashMap<String, Map<String, Object>>();
private final Map<String, List<Map<String, Object>>> editAreaAttrs = new HashMap<String, List<Map<String, Object>>>();
private final List<String> iscMarkersID = new ArrayList<String>();
@ -811,6 +812,8 @@ public class MapManager {
private List<ReferenceData> createReferenceData(DbShapeSource mapDef) {
// ServerResponse sr;
List<ReferenceData> data = new ArrayList<ReferenceData>();
List<Map<String, Object>> attributes = new ArrayList<Map<String, Object>>();
editAreaAttrs.put(mapDef.getDisplayName(), attributes);
// Module dean("DefaultEditAreaNaming");
ArrayList<String> created = new ArrayList<String>();
@ -871,7 +874,8 @@ public class MapManager {
// handle new case
else {
created.add(ean);
editAreaAttrs.put(ean, info);
info.put("editarea", ean);
attributes.add(info);
}
tempData.put(ean, mp);

View file

@ -20,27 +20,36 @@
package com.raytheon.edex.plugin.gfe.textproducts;
import java.io.File;
import java.io.PrintWriter;
import java.text.DecimalFormat;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import jep.JepException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.raytheon.edex.plugin.gfe.reference.MapManager;
import com.raytheon.uf.common.dataplugin.gfe.python.GfePyIncludeUtil;
import com.raytheon.uf.common.dataquery.db.QueryResult;
import com.raytheon.uf.common.dataquery.db.QueryResultRow;
import com.raytheon.uf.common.localization.IPathManager;
import com.raytheon.uf.common.localization.LocalizationContext;
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
import com.raytheon.uf.common.localization.LocalizationFile;
import com.raytheon.uf.common.localization.PathManagerFactory;
import com.raytheon.uf.common.localization.exception.LocalizationException;
import com.raytheon.uf.common.python.PyUtil;
import com.raytheon.uf.common.python.PythonScript;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.util.FileUtil;
import com.raytheon.uf.edex.database.tasks.SqlQueryTask;
/**
* TODO Add Description
* Code to generate the AreaDictionary for text formatters
*
* <pre>
*
@ -49,6 +58,8 @@ import com.raytheon.uf.common.util.FileUtil;
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* May 4, 2011 wldougher Moved from MapManager
* Oct 10, 2014 #3685 randerso Add code to generate the fips2cities and zones2cites
* python modules from the GIS database tables
*
* </pre>
*
@ -57,11 +68,61 @@ import com.raytheon.uf.common.util.FileUtil;
*/
public class AreaDictionaryMaker {
private static final Log theLogger = LogFactory
.getLog(AreaDictionaryMaker.class);
protected static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(AreaDictionaryMaker.class);
protected static final String FIPS_CITY_QUERY = //
"SELECT name, population, ST_Y(city.the_geom), ST_X(city.the_geom) "
+ "FROM mapdata.city, mapdata.county "
+ "WHERE county.state = '%1$s' AND substring(fips,3,3) = '%2$s' "
+ "AND ST_Contains(county.the_geom, city.the_geom) "
+ "ORDER BY city.name;";
protected static final String ZONES_CITY_QUERY = //
"SELECT city.name, population, ST_Y(city.the_geom), ST_X(city.the_geom) "
+ "FROM mapdata.city, mapdata.zone "
+ "WHERE zone.state = '%1$s' AND zone.zone = '%2$s' "
+ "AND ST_Contains(zone.the_geom, city.the_geom) "
+ "ORDER BY city.name;";
protected static final Map<String, String> PART_OF_STATE;
static {
PART_OF_STATE = new HashMap<>(30, 1.0f);
PART_OF_STATE.put(null, "");
PART_OF_STATE.put("bb", "big bend");
PART_OF_STATE.put("c", "");
PART_OF_STATE.put("cc", "central");
PART_OF_STATE.put("E", "");
PART_OF_STATE.put("ea", "east");
PART_OF_STATE.put("ec", "east central");
PART_OF_STATE.put("ee", "eastern");
PART_OF_STATE.put("er", "east central upper");
PART_OF_STATE.put("eu", "eastern upper");
PART_OF_STATE.put("M", "");
PART_OF_STATE.put("mi", "middle");
PART_OF_STATE.put("nc", "north central");
PART_OF_STATE.put("ne", "northeast");
PART_OF_STATE.put("nn", "northern");
PART_OF_STATE.put("nr", "north central upper");
PART_OF_STATE.put("nw", "northwest");
PART_OF_STATE.put("pa", "panhandle");
PART_OF_STATE.put("pd", "piedmont");
PART_OF_STATE.put("sc", "south central");
PART_OF_STATE.put("se", "southeast");
PART_OF_STATE.put("so", "south");
PART_OF_STATE.put("sr", "south central upper");
PART_OF_STATE.put("ss", "southern");
PART_OF_STATE.put("sw", "southwest");
PART_OF_STATE.put("up", "upstate");
PART_OF_STATE.put("wc", "west central");
PART_OF_STATE.put("wu", "western upper");
PART_OF_STATE.put("ww", "western");
}
protected IPathManager pathMgr = PathManagerFactory.getPathManager();
private Map<String, String> stateDict;
/**
* Generate the AreaDictionary.py and CityLocation.py scripts for site,
* using editAreaAttrs.
@ -73,14 +134,14 @@ public class AreaDictionaryMaker {
* A Map from edit area names to shape file attributes
*/
public void genAreaDictionary(String site,
Map<String, Map<String, Object>> editAreaAttrs) {
theLogger.info("Area Dictionary generation phase");
Map<String, List<Map<String, Object>>> editAreaAttrs) {
statusHandler.info("Area Dictionary generation phase");
if (site == null) {
throw new IllegalArgumentException("site is null");
}
if ("".equals(site)) {
if (site.isEmpty()) {
throw new IllegalArgumentException("site is an empty string");
}
@ -89,14 +150,99 @@ public class AreaDictionaryMaker {
}
long t0 = System.currentTimeMillis();
genStateDict();
LocalizationContext cx = pathMgr.getContext(
LocalizationContext context = pathMgr.getContext(
LocalizationContext.LocalizationType.EDEX_STATIC,
LocalizationContext.LocalizationLevel.CONFIGURED);
context.setContextName(site);
List<Map<String, Object>> countyAttrs = editAreaAttrs.get("Counties");
List<Map<String, Object>> zoneAttrs = editAreaAttrs.get("Zones");
// To generate national fips2cities and zones2cities files
// uncomment the following lines. This should be done for testing
// purposes only and should not be checked in uncommented.
// context = pathMgr.getContext(
// LocalizationContext.LocalizationType.EDEX_STATIC,
// LocalizationContext.LocalizationLevel.BASE);
//
// String fipsQuery =
// "SELECT fips, state, fe_area, cwa FROM mapdata.county ORDER BY state, fips;";
//
// SqlQueryTask task = new SqlQueryTask(fipsQuery, "maps");
// try {
// QueryResult results = task.execute();
// countyAttrs = new ArrayList<Map<String, Object>>(
// results.getResultCount());
// for (QueryResultRow row : results.getRows()) {
// String num = (String) row.getColumn(0);
// if (num == null) {
// continue;
// }
//
// Map<String, Object> map = new HashMap<>(3, 1.0f);
// countyAttrs.add(map);
//
// String st = (String) row.getColumn(1);
// int len = num.length();
//
// map.put("editarea", st + 'C' + num.substring(len - 3, len));
// map.put("fe_area", row.getColumn(2));
// map.put("cwa", row.getColumn(3));
// }
// } catch (Exception e) {
// statusHandler.error(e.getLocalizedMessage(), e);
// }
//
// String zonesQuery =
// "SELECT zone, state, fe_area, cwa FROM mapdata.zone ORDER BY state, zone;";
// task = new SqlQueryTask(zonesQuery, "maps");
// try {
// QueryResult results = task.execute();
// zoneAttrs = new ArrayList<Map<String, Object>>(
// results.getResultCount());
// for (QueryResultRow row : results.getRows()) {
// String num = (String) row.getColumn(0);
// if (num == null) {
// continue;
// }
//
// Map<String, Object> map = new HashMap<>(3, 1.0f);
// zoneAttrs.add(map);
//
// String st = (String) row.getColumn(1);
// int len = num.length();
//
// map.put("editarea", st + 'Z' + num.substring(len - 3, len));
// map.put("fe_area", row.getColumn(2));
// map.put("cwa", row.getColumn(3));
// }
// } catch (Exception e) {
// statusHandler.error(e.getLocalizedMessage(), e);
// }
// To generate national fips2cities and zones2cities files
// uncomment the previous lines
genFips2Cities(context, countyAttrs);
genZones2Cities(context, zoneAttrs);
LocalizationContext baseCtx = pathMgr.getContext(
LocalizationType.EDEX_STATIC, LocalizationLevel.BASE);
File scriptFile = pathMgr.getLocalizationFile(cx,
File scriptFile = pathMgr.getLocalizationFile(baseCtx,
FileUtil.join("gfe", "createAreaDictionary.py")).getFile();
LocalizationContext configCtx = pathMgr.getContext(
LocalizationType.EDEX_STATIC, LocalizationLevel.CONFIGURED);
configCtx.setContextName(site);
File configDir = pathMgr.getLocalizationFile(configCtx, "gfe")
.getFile();
String includePath = PyUtil.buildJepIncludePath(true,
GfePyIncludeUtil.getCommonPythonIncludePath(),
scriptFile.getParent());
configDir.getPath(), scriptFile.getParent());
Map<String, Object> argMap = new HashMap<String, Object>();
LocalizationContext caveStaticConfig = pathMgr.getContext(
@ -120,7 +266,7 @@ public class AreaDictionaryMaker {
// createAreaDictionary()
pyScript.execute("createCityLocation", argMap);
} catch (JepException e) {
theLogger.error("Error generating area dictionary", e);
statusHandler.error("Error generating area dictionary", e);
} finally {
if (pyScript != null) {
pyScript.dispose();
@ -128,6 +274,138 @@ public class AreaDictionaryMaker {
}
long t1 = System.currentTimeMillis();
theLogger.info("Area Dictionary generation time: " + (t1 - t0) + " ms");
statusHandler.info("Area Dictionary generation time: " + (t1 - t0)
+ " ms");
}
private void genFips2Cities(LocalizationContext context,
List<Map<String, Object>> attributes) {
genArea2Cities(context, attributes, "fips2cities.py", "fipsdata",
"FIPS", 'C', FIPS_CITY_QUERY);
}
private void genZones2Cities(LocalizationContext context,
List<Map<String, Object>> attributes) {
genArea2Cities(context, attributes, "zones2cities.py", "zonedata",
"Zones", 'Z', ZONES_CITY_QUERY);
}
private void genArea2Cities(LocalizationContext context,
List<Map<String, Object>> attributes, String fileName,
String dictName, String group, char separator, String cityQuery) {
LocalizationFile lf = pathMgr.getLocalizationFile(context,
FileUtil.join("gfe", fileName));
try (PrintWriter out = new PrintWriter(lf.openOutputStream())) {
out.println(dictName + " = {");
try {
DecimalFormat df = new DecimalFormat("0.00000");
StringBuilder sb = new StringBuilder();
Pattern pattern = Pattern.compile("(\\p{Upper}{2})" + separator
+ "(\\d{3})");
for (Map<String, Object> att : attributes) {
String ean = (String) att.get("editarea");
if ((ean == null) || ean.isEmpty()) {
continue;
}
Matcher matcher = pattern.matcher(ean);
if (!matcher.matches()) {
continue;
}
String state = matcher.group(1);
String num = matcher.group(2);
String fullStateName = this.stateDict.get(state);
String partOfState = PART_OF_STATE.get(att.get("fe_area"));
String wfo = (String) att.get("cwa");
SqlQueryTask task = new SqlQueryTask(String.format(
cityQuery, state, num), "maps");
// retrieve cities for this area
QueryResult citiesResult = null;
try {
citiesResult = task.execute();
} catch (Exception e) {
statusHandler
.error("Error getting cites for " + ean, e);
}
sb.setLength(0);
sb.append("'").append(ean).append("': {");
sb.append("'fullStateName': '").append(fullStateName)
.append("', ");
sb.append("'state': '").append(state).append("', ");
sb.append("'cities': [");
if ((citiesResult != null)
&& (citiesResult.getResultCount() > 0)) {
for (QueryResultRow city : citiesResult.getRows()) {
String name = (String) city.getColumn(0);
Object population = city.getColumn(1);
Double lat = (Double) city.getColumn(2);
Double lon = (Double) city.getColumn(3);
if (name.indexOf("'") >= 0) {
sb.append("(\"").append(name).append("\", ");
} else {
sb.append("('").append(name).append("', ");
}
if (population == null) {
sb.append("None, ");
} else {
sb.append(population.toString()).append(", ");
}
sb.append("'").append(df.format(lat)).append("', ");
sb.append("'").append(df.format(lon))
.append("'), ");
}
sb.setLength(sb.length() - 2);
}
sb.append("], ");
sb.append("'partOfState': '").append(partOfState)
.append("', ");
sb.append("'wfo': '").append(wfo).append("'}, ");
out.println(sb.toString());
}
} catch (Exception e) {
statusHandler.error(e.getLocalizedMessage(), e);
}
out.println("}");
} catch (LocalizationException e) {
statusHandler.error(e.getLocalizedMessage(), e);
}
try {
lf.save();
} catch (Exception e) {
statusHandler.error(e.getLocalizedMessage(), e);
}
}
private void genStateDict() {
SqlQueryTask task = new SqlQueryTask(
"SELECT state, name FROM mapdata.states", "maps");
try {
QueryResult result = task.execute();
stateDict = new HashMap<String, String>(result.getResultCount(),
1.0f);
for (QueryResultRow row : result.getRows()) {
String st = (String) row.getColumn(0);
String name = (String) row.getColumn(1);
stateDict.put(st, name);
}
} catch (Exception e) {
statusHandler.error(e.getLocalizedMessage(), e);
}
}
}

View file

@ -21,7 +21,7 @@ package com.raytheon.edex.plugin.gfe.textproducts;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@ -32,10 +32,14 @@ import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.raytheon.edex.utility.ProtectedFiles;
import com.raytheon.uf.common.dataplugin.gfe.python.GfePyIncludeUtil;
import com.raytheon.uf.common.dataquery.db.QueryResult;
import com.raytheon.uf.common.dataquery.db.QueryResultRow;
import com.raytheon.uf.common.localization.IPathManager;
import com.raytheon.uf.common.localization.LocalizationContext;
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
import com.raytheon.uf.common.localization.LocalizationFile;
import com.raytheon.uf.common.localization.PathManagerFactory;
import com.raytheon.uf.common.python.PyUtil;
import com.raytheon.uf.common.python.PythonScript;
@ -45,6 +49,7 @@ import com.raytheon.uf.common.status.UFStatus.Priority;
import com.raytheon.uf.common.util.FileUtil;
import com.raytheon.uf.edex.database.cluster.ClusterLockUtils;
import com.raytheon.uf.edex.database.cluster.ClusterTask;
import com.raytheon.uf.edex.database.tasks.SqlQueryTask;
/**
* Generate and configure text products when needed.
@ -59,11 +64,13 @@ import com.raytheon.uf.edex.database.cluster.ClusterTask;
* SOFTWARE HISTORY
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Jul 7, 2008 1222 jelkins Initial creation
* Jul 24,2012 #944 dgilling Fix text product template generation
* Jul 7, 2008 1222 jelkins Initial creation
* Jul 24, 2012 #944 dgilling Fix text product template generation
* to create textProducts and textUtilities.
* Sep 07,2012 #1150 dgilling Fix isConfigured to check for textProducts
* Sep 07, 2012 #1150 dgilling Fix isConfigured to check for textProducts
* and textUtilities dirs.
* Oct 20, 2014 #3685 randerso Added code to generate SiteCFG.py from GIS database
* Cleaned up how protected file updates are returned
*
* </pre>
*
@ -75,6 +82,8 @@ public class Configurator {
private static final transient IUFStatusHandler statusHandler = UFStatus
.getHandler(Configurator.class);
private static final String CWA_QUERY = "select wfo, region, fullstaid, citystate, city, state from mapdata.cwa order by wfo;";
private static final String CONFIG_TEXT_PRODUCTS_TASK = "GfeConfigureTextProducts";
private String siteID;
@ -183,26 +192,79 @@ public class Configurator {
*/
@SuppressWarnings("unchecked")
public void execute() {
PythonScript python = null;
List<String> preEvals = new ArrayList<String>();
if (isConfigured()) {
statusHandler.info("All text products are up to date");
return;
}
IPathManager pathMgr = PathManagerFactory.getPathManager();
LocalizationContext context = pathMgr.getContext(
LocalizationType.COMMON_STATIC, LocalizationLevel.CONFIGURED);
context.setContextName(siteID);
// regenerate siteCFG.py
LocalizationFile lf = null;
try {
lf = pathMgr.getLocalizationFile(context,
FileUtil.join("python", "gfe", "SiteCFG.py"));
SqlQueryTask task = new SqlQueryTask(CWA_QUERY, "maps");
QueryResult results = task.execute();
try (PrintWriter out = new PrintWriter(lf.openOutputStream())) {
out.println("##");
out.println("# Contains information about products, regions, etc. for each site");
out.println("# in the country.");
out.println("# region= two-letter regional identifier, mainly used for installation of");
out.println("# text product templates");
out.println("SiteInfo= {");
for (QueryResultRow row : results.getRows()) {
String wfo = (String) row.getColumn(0);
String region = (String) row.getColumn(1);
String fullStationID = (String) row.getColumn(2);
String wfoCityState = (String) row.getColumn(3);
String wfoCity = (String) row.getColumn(4);
String state = (String) row.getColumn(5);
out.println(formatEntry(wfo, region, fullStationID,
wfoCityState, wfoCity, state));
// Add in AFC's dual domain sites
if (wfo.equals("AFC")) {
out.println(formatEntry("AER", region, fullStationID,
wfoCityState, wfoCity, state));
out.println(formatEntry("ALU", region, fullStationID,
wfoCityState, wfoCity, state));
}
}
// Add in the national centers since they
// aren't in the shape file
out.println(formatEntry("NH1", "NC", "KNHC",
"National Hurricane Center Miami FL", "Miami", ""));
out.println(formatEntry("NH2", "NC", "KNHC",
"National Hurricane Center Miami FL", "Miami", ""));
out.println(formatEntry("ONA", "NC", "KWBC",
"Ocean Prediction Center Washington DC",
"Washington DC", ""));
out.println(formatEntry("ONP", "NC", "KWBC",
"Ocean Prediction Center Washington DC",
"Washington DC", ""));
out.println("}");
} // out is closed here
lf.save();
} catch (Exception e) {
statusHandler.error(e.getLocalizedMessage(), e);
}
PythonScript python = null;
LocalizationContext edexCx = pathMgr.getContext(
LocalizationType.EDEX_STATIC, LocalizationLevel.BASE);
LocalizationContext commonCx = pathMgr.getContext(
LocalizationType.COMMON_STATIC, LocalizationLevel.BASE);
String filePath = pathMgr.getFile(edexCx,
"textproducts" + File.separator + "Generator.py").getPath();
String textProductPath = pathMgr.getFile(edexCx,
"textProducts.Generator").getPath();
String jutilPath = pathMgr.getFile(commonCx, "python").getPath();
// Add some getters we need "in the script" that we want hidden
preEvals.add("from JUtil import pylistToJavaStringList");
preEvals.add("from textproducts.Generator import Generator");
preEvals.add("generator = Generator()");
preEvals.add("def getProtectedData():\n return pylistToJavaStringList(generator.getProtectedFiles())");
String commonPython = GfePyIncludeUtil.getCommonPythonIncludePath();
Map<String, Object> argList = new HashMap<String, Object>();
argList.put("siteId", siteID);
@ -210,20 +272,18 @@ public class Configurator {
try {
python = new PythonScript(filePath, PyUtil.buildJepIncludePath(
pythonDirectory, textProductPath, jutilPath), this
.getClass().getClassLoader(), preEvals);
pythonDirectory, commonPython), this.getClass()
.getClassLoader());
// Open the Python interpreter using the designated script.
python.execute("generator.create", argList);
protectedFilesList = (List<String>) python.execute(
"getProtectedData", null);
protectedFilesList = (List<String>) python.execute("runFromJava",
argList);
updateProtectedFile();
updateLastRuntime();
} catch (JepException e) {
statusHandler.handle(Priority.PROBLEM,
"Error Configuring Text Products", e);
e.printStackTrace();
} finally {
if (python != null) {
python.dispose();
@ -231,6 +291,22 @@ public class Configurator {
}
}
private String formatEntry(String wfo, String region, String fullStationID,
String wfoCityState, String wfoCity, String state) {
StringBuilder sb = new StringBuilder();
sb.append(" '").append(wfo).append("': {\n");
sb.append(" 'region': '").append(region).append("',\n");
sb.append(" 'fullStationID': '").append(fullStationID)
.append("',\n");
sb.append(" 'wfoCityState': '").append(wfoCityState)
.append("',\n");
sb.append(" 'wfoCity': '").append(wfoCity).append("',\n");
sb.append(" 'state': '").append(state).append("',\n");
sb.append(" },");
return sb.toString();
}
/**
* Update the protected files.
*/

View file

@ -35,8 +35,8 @@ from zones2cities import *
# ------------ ---------- ----------- --------------------------
# 01/08/10 #1209 randerso Initial Creation.
# 10/19/12 #1091 dgilling Support localMaps.py.
#
#
# 10/20/2014 #3685 randerso Converted text to mixed case
# Fixed mapDict to keep zones from different maps separate
#
CityLocationDict = {}
@ -111,13 +111,23 @@ def makeCityString(dictRecord):
# handle marine states
def checkMarineState(ugcCode):
#returns None if unknown, description if known
areas = {'AM': 'ATLANTIC COASTAL WATERS', 'GM': 'GULF OF MEXICO',
'LE': 'LAKE ERIE', 'LO': 'LAKE ONTARIO', 'LH': 'LAKE HURON',
'SC': 'LAKE ST CLAIR', 'LM': 'LAKE MICHIGAN', 'LS': 'LAKE SUPERIOR',
'PZ': 'PACIFIC COASTAL WATERS', 'PK': 'ALASKAN COASTAL WATERS',
'PH': 'HAWAIIAN COASTAL WATERS', 'PM': 'MARIANAS WATERS',
'AN': 'ATLANTIC COASTAL WATERS', 'PS': 'AMERICAN SAMOA COASTAL WATERS',
'SL': 'ST LAWRENCE RIVER'}
areas = {
'AM': 'Atlantic coastal waters',
'GM': 'Gulf of Mexico',
'LE': 'Lake Erie',
'LO': 'Lake Ontario',
'LH': 'Lake Huron',
'SC': 'Lake St Clair',
'LM': 'Lake Michigan',
'LS': 'Lake Superior',
'PZ': 'Pacific coastal waters',
'PK': 'Alaskan coastal waters',
'PH': 'Hawaiian coastal waters',
'PM': 'Marianas waters',
'AN': 'Atlantic coastal waters',
'PS': 'American Samoa coastal waters',
'SL': 'St Lawrence River',
}
area = ugcCode[0:2]
return areas.get(area, None)
@ -128,82 +138,86 @@ def createAreaDictionary(outputDir, mapDict):
areadict = {}
mapIter = mapDict.entrySet().iterator()
while mapIter.hasNext():
entry = mapIter.next()
ean = str(entry.getKey())
att = entry.getValue()
if len(ean):
try:
d = {}
if att.containsKey('zone') and att.containsKey('state'):
d['ugcCode'] = str(att.get('state')) + "Z" + str(att.get('zone'))
elif att.containsKey('id'):
d['ugcCode'] = str(att.get('id'))
elif att.containsKey('fips') and att.containsKey('state') and \
att.containsKey('countyname'):
d['ugcCode'] = str(att.get('state')) + "C" + str(att.get('fips'))[-3:]
d['ugcName'] = string.strip(str(att.get('countyname')))
else:
continue
if att.containsKey('state'):
d["stateAbbr"] = str(att.get('state'))
if att.containsKey('name'):
d["ugcName"] = string.strip(str(att.get('name')))
if att.containsKey('time_zone'):
tzvalue = getRealTimeZone(str(att.get('time_zone')))
if tzvalue is not None:
d["ugcTimeZone"] = tzvalue
if zonedata.has_key(d['ugcCode']):
cityDict = zonedata[d['ugcCode']]
elif fipsdata.has_key(d['ugcCode']):
cityDict = fipsdata[d['ugcCode']]
else:
cityDict = None
if cityDict:
cityString = makeCityString(cityDict)
if cityString is not None:
cityString, locs = cityString
if len(cityString):
d["ugcCityString"] = cityString
CityLocationDict[ean] = locs
# partOfState codes
if zonedata.has_key(d['ugcCode']):
if zonedata[d['ugcCode']].has_key('partOfState'):
d["partOfState"] = \
zonedata[d['ugcCode']]['partOfState']
elif fipsdata.has_key(d['ugcCode']):
if fipsdata[d['ugcCode']].has_key('partOfState'):
d["partOfState"] = \
fipsdata[d['ugcCode']]['partOfState']
# full state name
if zonedata.has_key(d['ugcCode']):
if zonedata[d['ugcCode']].has_key('fullStateName'):
d["fullStateName"] = \
zonedata[d['ugcCode']]['fullStateName']
elif fipsdata.has_key(d['ugcCode']):
if fipsdata[d['ugcCode']].has_key('fullStateName'):
d["fullStateName"] = \
fipsdata[d['ugcCode']]['fullStateName']
else:
marineState = checkMarineState(d['ugcCode'])
if marineState is not None:
d['fullStateName'] = marineState
if areadict.has_key(ean) and d != areadict[ean]:
LogStream.logDiag("Mismatch of definitions in " +\
"AreaDictionary creation. EditAreaName=", ean,
"AreaDict=\n", areadict[ean], "\nIgnored=\n", d)
else:
areadict[ean] = d
except:
LogStream.logProblem("Problem with ", ean, LogStream.exc())
mapEntry = mapIter.next()
mapname = str(mapEntry.getKey())
attList = mapEntry.getValue()
attIter = attList.iterator()
while attIter.hasNext():
att = attIter.next()
ean = str(att.get("editarea"))
if len(ean):
try:
d = {}
if att.containsKey('zone') and att.containsKey('state'):
d['ugcCode'] = str(att.get('state')) + "Z" + str(att.get('zone'))
elif att.containsKey('id'):
d['ugcCode'] = str(att.get('id'))
elif att.containsKey('fips') and att.containsKey('state') and \
att.containsKey('countyname'):
d['ugcCode'] = str(att.get('state')) + "C" + str(att.get('fips'))[-3:]
d['ugcName'] = string.strip(str(att.get('countyname')))
else:
continue
if att.containsKey('state'):
d["stateAbbr"] = str(att.get('state'))
if att.containsKey('name'):
d["ugcName"] = string.strip(str(att.get('name')))
if att.containsKey('time_zone'):
tzvalue = getRealTimeZone(str(att.get('time_zone')))
if tzvalue is not None:
d["ugcTimeZone"] = tzvalue
if zonedata.has_key(d['ugcCode']):
cityDict = zonedata[d['ugcCode']]
elif fipsdata.has_key(d['ugcCode']):
cityDict = fipsdata[d['ugcCode']]
else:
cityDict = None
if cityDict:
cityString = makeCityString(cityDict)
if cityString is not None:
cityString, locs = cityString
if len(cityString):
d["ugcCityString"] = cityString
CityLocationDict[ean] = locs
# partOfState codes
if zonedata.has_key(d['ugcCode']):
if zonedata[d['ugcCode']].has_key('partOfState'):
d["partOfState"] = \
zonedata[d['ugcCode']]['partOfState']
elif fipsdata.has_key(d['ugcCode']):
if fipsdata[d['ugcCode']].has_key('partOfState'):
d["partOfState"] = \
fipsdata[d['ugcCode']]['partOfState']
# full state name
if zonedata.has_key(d['ugcCode']):
if zonedata[d['ugcCode']].has_key('fullStateName'):
d["fullStateName"] = \
zonedata[d['ugcCode']]['fullStateName']
elif fipsdata.has_key(d['ugcCode']):
if fipsdata[d['ugcCode']].has_key('fullStateName'):
d["fullStateName"] = \
fipsdata[d['ugcCode']]['fullStateName']
else:
marineState = checkMarineState(d['ugcCode'])
if marineState is not None:
d['fullStateName'] = marineState
if areadict.has_key(ean) and d != areadict[ean]:
LogStream.logDiag("Mismatch of definitions in " +\
"AreaDictionary creation. EditAreaName=", ean,
"AreaDict=\n", areadict[ean], "\nIgnored=\n", d)
else:
areadict[ean] = d
except:
LogStream.logProblem("Problem with ", ean, LogStream.exc())
s = """
# ----------------------------------------------------------------------------

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -18,29 +18,34 @@
# See the AWIPS II Master Rights File ("Master Rights File.pdf") for
# further licensing information.
##
"""Generate site specific text products.
This script is run at install time to customize a set of the text products
for a given site.
SOFTWARE HISTORY
Date Ticket# Engineer Description
------------ ---------- ----------- --------------------------
Jun 23, 2008 1180 jelkins Initial creation
Jul 08, 2008 1222 jelkins Modified for use within Java
Jul 09, 2008 1222 jelkins Split command line loader from class
Jul 24, 2012 #944 dgilling Refactored to support separate
generation of products and utilities.
Sep 07, 2012 #1150 dgilling Ensure all necessary dirs get created.
May 12, 2014 2536 bclement renamed text plugin to include uf in name
@author: jelkins
"""
#
# Generate site specific text products.
#
# This script is run at install time to customize a set of the text products
# for a given site.
#
# SOFTWARE HISTORY
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# Jun 23, 2008 1180 jelkins Initial creation
# Jul 08, 2008 1222 jelkins Modified for use within Java
# Jul 09, 2008 1222 jelkins Split command line loader from class
# Jul 24, 2012 #944 dgilling Refactored to support separate
# generation of products and utilities.
# Sep 07, 2012 #1150 dgilling Ensure all necessary dirs get created.
# May 12, 2014 2536 bclement renamed text plugin to include uf in name
# Oct 20, 2014 #3685 randerso Changed how SiteInfo is loaded.
# Fixed logging to log to a file
# Cleaned up how protected file updates are returned
#
# @author: jelkins
#
##
__version__ = "1.0"
import errno
import os
import JUtil
from os.path import basename
from os.path import join
from os.path import dirname
@ -64,7 +69,6 @@ from sys import path
path.append(join(LIBRARY_DIR,"../"))
path.append(join(PREFERENCE_DIR,"../"))
from library.SiteInfo import SiteInfo as SITE_INFO
from preferences.configureTextProducts import NWSProducts as NWS_PRODUCTS
from os.path import basename
@ -73,12 +77,21 @@ from os.path import abspath
from os.path import join
# ---- Setup Logging ----------------------------------------------------------
LOG_CONF = join(SCRIPT_DIR,"preferences","logging.conf")
import logging
from time import strftime, gmtime
timeStamp = strftime("%Y%m%d", gmtime())
logFile = '/awips2/edex/logs/configureTextProducts-'+timeStamp+'.log'
import logging.config
logging.config.fileConfig(LOG_CONF)
LOG = logging.getLogger("configureTextProducts")
LOG.setLevel(logging.DEBUG)
handler = logging.FileHandler(logFile)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter("%(levelname)-5s %(asctime)s [%(process)d:%(thread)d] %(filename)s: %(message)s")
handler.setFormatter(formatter)
for h in LOG.handlers:
LOG.removeHandler(h)
LOG.addHandler(handler)
LOG = logging.getLogger("Generator")
# List of protected files
fileList = []
@ -96,6 +109,17 @@ ProcessDirectories = [
},
]
# This will "load" SiteInfo in a more complicated way
# than 'from SiteCFG import SiteInfo'.
from LockingFile import File
pathManager = PathManagerFactory.getPathManager()
lf = pathManager.getStaticLocalizationFile(LocalizationType.COMMON_STATIC, "python/gfe/SiteCFG.py")
with File(lf.getFile(), lf.getName(), 'r') as file:
fileContents = file.read()
exec fileContents
class Generator():
"""Generates site specific text products from base template files.
@ -118,7 +142,7 @@ class Generator():
@raise LookupError: when the site ID is invalid
"""
if siteId in SITE_INFO.keys():
if siteId in SiteInfo.keys():
self.__siteId = siteId
else:
raise LookupError, ' unknown WFO: ' + siteId
@ -179,6 +203,8 @@ class Generator():
created += self.__create(dirInfo['src'], dirInfo['dest'])
LOG.info("%d text products created" % created)
LOG.debug("Configuration of Text Products Finish")
return JUtil.pylistToJavaStringList(self.getProtectedFiles())
def delete(self):
"""Delete text products"""
@ -216,11 +242,11 @@ class Generator():
LOG.debug("PIL Information for all sites Begin.......")
for site in SITE_INFO.keys():
for site in SiteInfo.keys():
LOG.info("--------------------------------------------")
LOG.info("%s %s %s" % (site,
SITE_INFO[site]['fullStationID'],
SITE_INFO[site]['wfoCityState']))
SiteInfo[site]['fullStationID'],
SiteInfo[site]['wfoCityState']))
pils = self.__createPilDictionary(site)
self.__printPilDictionary(pils)
found += len(pils)
@ -303,11 +329,11 @@ class Generator():
subDict = {}
subDict['<site>'] = siteid.strip()
subDict['<region>'] = SITE_INFO[siteid]['region'].strip()
subDict['<wfoCityState>'] = SITE_INFO[siteid]['wfoCityState'].strip()
subDict['<wfoCity>'] = SITE_INFO[siteid]['wfoCity'].strip()
subDict['<fullStationID>'] = SITE_INFO[siteid]['fullStationID'].strip()
subDict['<state>'] = SITE_INFO[siteid]['state'].strip()
subDict['<region>'] = SiteInfo[siteid]['region'].strip()
subDict['<wfoCityState>'] = SiteInfo[siteid]['wfoCityState'].strip()
subDict['<wfoCity>'] = SiteInfo[siteid]['wfoCity'].strip()
subDict['<fullStationID>'] = SiteInfo[siteid]['fullStationID'].strip()
subDict['<state>'] = SiteInfo[siteid]['state'].strip()
if product is not None:
subDict['<product>'] = product.strip()
if ProductToStandardMapping.has_key(product):
@ -342,7 +368,7 @@ class Generator():
subDict = {}
subDict['Site'] = siteid.strip()
subDict['Region'] = SITE_INFO[siteid]['region'].strip()
subDict['Region'] = SiteInfo[siteid]['region'].strip()
if product is not None:
subDict['Product'] = product.strip()
if pilInfo is not None and pilInfo.has_key("pil") and multiPilFlag:
@ -378,10 +404,10 @@ class Generator():
LOG.info("%s %s" % (p,pillist[p]))
def __createPilDictionary(self, siteid):
"""Update the SITE_INFO with a PIL dictionary
"""Update the SiteInfo with a PIL dictionary
Read the a2a data from the database, create PIL information, and add the information
to the SITE_INFO dictionary.
to the SiteInfo dictionary.
@param site: the site for which PIL information is created
@type site: string
@ -390,7 +416,7 @@ class Generator():
@rtype: dictionary
"""
siteD = SITE_INFO[siteid]
siteD = SiteInfo[siteid]
stationID4 = siteD['fullStationID']
from com.raytheon.uf.edex.plugin.text.dao import AfosToAwipsDao
@ -435,7 +461,7 @@ class Generator():
e['textdbPil'] = pil
e['awipsWANPil'] = site4 + pil[3:]
d.append(e)
siteD[nnn] = d #store the pil dictionary back into the SITE_INFO
siteD[nnn] = d #store the pil dictionary back into the SiteInfo
return pillist
@ -572,8 +598,8 @@ class Generator():
continue
# extract out the pil information from the dictionary
if SITE_INFO[siteid].has_key(pilNames[0]):
pils = SITE_INFO[siteid][pilNames[0]]
if SiteInfo[siteid].has_key(pilNames[0]):
pils = SiteInfo[siteid][pilNames[0]]
else:
#set pils to empty list if none defined
pils = [{'awipsWANPil': 'kssscccnnn',
@ -728,4 +754,7 @@ class Generator():
LOG.debug(" Deleting Existing Baseline Templates Finished........")
return productsRemoved
def runFromJava(siteId, destinationDir):
generator = Generator()
return generator.create(siteId, destinationDir)

View file

@ -1,922 +0,0 @@
##
# This software was developed and / or modified by Raytheon Company,
# pursuant to Contract DG133W-05-CQ-1067 with the US Government.
#
# U.S. EXPORT CONTROLLED TECHNICAL DATA
# This software product contains export-restricted data whose
# export/transfer/disclosure is restricted by U.S. law. Dissemination
# to non-U.S. persons whether in the United States or abroad requires
# an export license or other authorization.
#
# Contractor Name: Raytheon Company
# Contractor Address: 6825 Pine Street, Suite 340
# Mail Stop B8
# Omaha, NE 68106
# 402.291.0100
#
# See the AWIPS II Master Rights File ("Master Rights File.pdf") for
# further licensing information.
##
#Contains information about products, regions, etc. for each site
#in the country.
#region= two-letter regional identifier, mainly used for installation of
# text product templates
SiteInfo= {
'ABQ': {
'region': 'SR',
'fullStationID': 'KABQ',
'wfoCityState': 'ALBUQUERQUE NM',
'wfoCity': 'ALBUQUERQUE',
'state': 'NEW MEXICO',
},
'ABR': {
'region': 'CR',
'fullStationID': 'KABR',
'wfoCityState': 'ABERDEEN SD',
'wfoCity': 'ABERDEEN',
'state': 'SOUTH DAKOTA',
},
'AER': {
'region': 'AR',
'fullStationID': 'PAFC',
'wfoCityState': 'ANCHORAGE AK',
'wfoCity': 'ANCHORAGE',
'state': 'ALASKA',
},
'AFC': {
'region': 'AR',
'fullStationID': 'PAFC',
'wfoCityState': 'ANCHORAGE AK',
'wfoCity': 'ANCHORAGE',
'state': 'ALASKA',
},
'AFG': {
'region': 'AR',
'fullStationID': 'PAFG',
'wfoCityState': 'FAIRBANKS AK',
'wfoCity': 'FAIRBANKS',
'state': 'ALASKA',
},
'AJK': {
'region': 'AR',
'fullStationID': 'PAJK',
'wfoCityState': 'JUNEAU AK',
'wfoCity': 'JUNEAU',
'state': 'ALASKA',
},
'AKQ': {
'region': 'ER',
'fullStationID': 'KAKQ',
'wfoCityState': 'WAKEFIELD VA',
'wfoCity': 'WAKEFIELD',
'state': 'VIRGINIA',
},
'ALU': {
'region': 'AR',
'fullStationID': 'PAFC',
'wfoCityState': 'ANCHORAGE AK',
'wfoCity': 'ANCHORAGE',
'state': 'ALASKA',
},
'ALY': {
'region': 'ER',
'fullStationID': 'KALY',
'wfoCityState': 'ALBANY NY',
'wfoCity': 'ALBANY',
'state': 'NEW YORK',
},
'AMA': {
'region': 'SR',
'fullStationID': 'KAMA',
'wfoCityState': 'AMARILLO TX',
'wfoCity': 'AMARILLO',
'state': 'TEXAS',
},
'APX': {
'region': 'CR',
'fullStationID': 'KAPX',
'wfoCityState': 'GAYLORD MI',
'wfoCity': 'GAYLORD',
'state': 'MICHIGAN',
},
'ARX': {
'region': 'CR',
'fullStationID': 'KARX',
'wfoCityState': 'LA CROSSE WI',
'wfoCity': 'LA CROSSE',
'state': 'WISCONSIN',
},
'BGM': {
'region': 'ER',
'fullStationID': 'KBGM',
'wfoCityState': 'BINGHAMTON NY',
'wfoCity': 'BINGHAMTON',
'state': 'NEW YORK',
},
'BIS': {
'region': 'CR',
'fullStationID': 'KBIS',
'wfoCityState': 'BISMARCK ND',
'wfoCity': 'BISMARCK',
'state': 'NORTH DAKOTA',
},
'BMX': {
'region': 'SR',
'fullStationID': 'KBMX',
'wfoCityState': 'BIRMINGHAM AL',
'wfoCity': 'BIRMINGHAM',
'state': 'ALABAMA',
},
'BOI': {
'region': 'WR',
'fullStationID': 'KBOI',
'wfoCityState': 'BOISE ID',
'wfoCity': 'BOISE',
'state': 'IDAHO',
},
'BOU': {
'region': 'CR',
'fullStationID': 'KBOU',
'wfoCityState': 'DENVER CO',
'wfoCity': 'DENVER',
'state': 'COLORADO',
},
'BOX': {
'region': 'ER',
'fullStationID': 'KBOX',
'wfoCityState': 'TAUNTON MA',
'wfoCity': 'TAUNTON',
'state': 'MASSACHUSETTS',
},
'BRO': {
'region': 'SR',
'fullStationID': 'KBRO',
'wfoCityState': 'BROWNSVILLE TX',
'wfoCity': 'BROWNSVILLE',
'state': 'TEXAS',
},
'BTV': {
'region': 'ER',
'fullStationID': 'KBTV',
'wfoCityState': 'BURLINGTON VT',
'wfoCity': 'BURLINGTON',
'state': 'VERMONT',
},
'BUF': {
'region': 'ER',
'fullStationID': 'KBUF',
'wfoCityState': 'BUFFALO NY',
'wfoCity': 'BUFFALO',
'state': 'NEW YORK',
},
'BYZ': {
'region': 'WR',
'fullStationID': 'KBYZ',
'wfoCityState': 'BILLINGS MT',
'wfoCity': 'BILLINGS',
'state': 'MONTANA',
},
'CAE': {
'region': 'ER',
'fullStationID': 'KCAE',
'wfoCityState': 'COLUMBIA SC',
'wfoCity': 'COLUMBIA',
'state': 'SOUTH CAROLINA',
},
'CAR': {
'region': 'ER',
'fullStationID': 'KCAR',
'wfoCityState': 'CARIBOU ME',
'wfoCity': 'CARIBOU',
'state': 'MAINE',
},
'CHS': {
'region': 'ER',
'fullStationID': 'KCHS',
'wfoCityState': 'CHARLESTON SC',
'wfoCity': 'CHARLESTON',
'state': 'SOUTH CAROLINA',
},
'CLE': {
'region': 'ER',
'fullStationID': 'KCLE',
'wfoCityState': 'CLEVELAND OH',
'wfoCity': 'CLEVELAND',
'state': 'OHIO',
},
'CRP': {
'region': 'SR',
'fullStationID': 'KCRP',
'wfoCityState': 'CORPUS CHRISTI TX',
'wfoCity': 'CORPUS CHRISTI',
'state': 'TEXAS',
},
'CTP': {
'region': 'ER',
'fullStationID': 'KCTP',
'wfoCityState': 'STATE COLLEGE PA',
'wfoCity': 'STATE COLLEGE',
'state': 'PENNSYLVANIA',
},
'CYS': {
'region': 'CR',
'fullStationID': 'KCYS',
'wfoCityState': 'CHEYENNE WY',
'wfoCity': 'CHEYENNE',
'state': 'WYOMING',
},
'DDC': {
'region': 'CR',
'fullStationID': 'KDDC',
'wfoCityState': 'DODGE CITY KS',
'wfoCity': 'DODGE CITY',
'state': 'KANSAS',
},
'DLH': {
'region': 'CR',
'fullStationID': 'KDLH',
'wfoCityState': 'DULUTH MN',
'wfoCity': 'DULUTH',
'state': 'MINNESOTA',
},
'DMX': {
'region': 'CR',
'fullStationID': 'KDMX',
'wfoCityState': 'DES MOINES IA',
'wfoCity': 'DES MOINES',
'state': 'IOWA',
},
'DTX': {
'region': 'CR',
'fullStationID': 'KDTX',
'wfoCityState': 'DETROIT/PONTIAC MI',
'wfoCity': 'DETROIT/PONTIAC',
'state': 'MICHIGAN',
},
'DVN': {
'region': 'CR',
'fullStationID': 'KDVN',
'wfoCityState': 'QUAD CITIES IA IL',
'wfoCity': 'QUAD CITIES',
'state': 'ILLINOIS',
},
'EAX': {
'region': 'CR',
'fullStationID': 'KEAX',
'wfoCityState': 'KANSAS CITY/PLEASANT HILL MO',
'wfoCity': 'KANSAS CITY/PLEASANT HILL',
'state': 'MISSOURI',
},
'EKA': {
'region': 'WR',
'fullStationID': 'KEKA',
'wfoCityState': 'EUREKA CA',
'wfoCity': 'EUREKA',
'state': 'CALIFORNIA',
},
'EPZ': {
'region': 'SR',
'fullStationID': 'KEPZ',
'wfoCityState': 'EL PASO TX/SANTA TERESA NM',
'wfoCity': 'EL PASO TX/SANTA TERESA',
'state': 'NEW MEXICO',
},
'EWX': {
'region': 'SR',
'fullStationID': 'KEWX',
'wfoCityState': 'AUSTIN/SAN ANTONIO TX',
'wfoCity': 'AUSTIN/SAN ANTONIO',
'state': 'TEXAS',
},
'FFC': {
'region': 'SR',
'fullStationID': 'KFFC',
'wfoCityState': 'PEACHTREE CITY GA',
'wfoCity': 'PEACHTREE CITY',
'state': 'GEORGIA',
},
'FGF': {
'region': 'CR',
'fullStationID': 'KFGF',
'wfoCityState': 'GRAND FORKS ND',
'wfoCity': 'GRAND FORKS',
'state': 'NORTH DAKOTA',
},
'FGZ': {
'region': 'WR',
'fullStationID': 'KFGZ',
'wfoCityState': 'FLAGSTAFF AZ',
'wfoCity': 'FLAGSTAFF',
'state': 'ARIZONA',
},
'FSD': {
'region': 'CR',
'fullStationID': 'KFSD',
'wfoCityState': 'SIOUX FALLS SD',
'wfoCity': 'SIOUX FALLS',
'state': 'SOUTH DAKOTA',
},
'FWD': {
'region': 'SR',
'fullStationID': 'KFWD',
'wfoCityState': 'FORT WORTH TX',
'wfoCity': 'FORT WORTH',
'state': 'TEXAS',
},
'GGW': {
'region': 'WR',
'fullStationID': 'KGGW',
'wfoCityState': 'GLASGOW MT',
'wfoCity': 'GLASGOW',
'state': 'MONTANA',
},
'GID': {
'region': 'CR',
'fullStationID': 'KGID',
'wfoCityState': 'HASTINGS NE',
'wfoCity': 'HASTINGS',
'state': 'NEBRASKA',
},
'GJT': {
'region': 'CR',
'fullStationID': 'KGJT',
'wfoCityState': 'GRAND JUNCTION CO',
'wfoCity': 'GRAND JUNCTION',
'state': 'COLORADO',
},
'GLD': {
'region': 'CR',
'fullStationID': 'KGLD',
'wfoCityState': 'GOODLAND KS',
'wfoCity': 'GOODLAND',
'state': 'KANSAS',
},
'GRB': {
'region': 'CR',
'fullStationID': 'KGRB',
'wfoCityState': 'GREEN BAY WI',
'wfoCity': 'GREEN BAY',
'state': 'WISCONSIN',
},
'GRR': {
'region': 'CR',
'fullStationID': 'KGRR',
'wfoCityState': 'GRAND RAPIDS MI',
'wfoCity': 'GRAND RAPIDS',
'state': 'MICHIGAN',
},
'GSP': {
'region': 'ER',
'fullStationID': 'KGSP',
'wfoCityState': 'GREENVILLE-SPARTANBURG SC',
'wfoCity': 'GREENVILLE-SPARTANBURG',
'state': 'SOUTH CAROLINA',
},
'GUM': {
'region': 'PR',
'fullStationID': 'PGUM',
'wfoCityState': 'TIYAN GU',
'wfoCity': 'TIYAN',
'state': 'GUAM',
},
'GYX': {
'region': 'ER',
'fullStationID': 'KGYX',
'wfoCityState': 'GRAY ME',
'wfoCity': 'GRAY',
'state': 'MAINE',
},
'HFO': {
'region': 'PR',
'fullStationID': 'PHFO',
'wfoCityState': 'HONOLULU HI',
'wfoCity': 'HONOLULU',
'state': 'HAWAII',
},
'HGX': {
'region': 'SR',
'fullStationID': 'KHGX',
'wfoCityState': 'HOUSTON/GALVESTON TX',
'wfoCity': 'HOUSTON/GALVESTON',
'state': 'TEXAS',
},
'HNX': {
'region': 'WR',
'fullStationID': 'KHNX',
'wfoCityState': 'HANFORD CA',
'wfoCity': 'HANFORD',
'state': 'CALIFORNIA',
},
'HUN': {
'region': 'SR',
'fullStationID': 'KHUN',
'wfoCityState': 'HUNTSVILLE AL',
'wfoCity': 'HUNTSVILLE',
'state': 'ALABAMA',
},
'ICT': {
'region': 'CR',
'fullStationID': 'KICT',
'wfoCityState': 'WICHITA KS',
'wfoCity': 'WICHITA',
'state': 'KANSAS',
},
'ILM': {
'region': 'ER',
'fullStationID': 'KILM',
'wfoCityState': 'WILMINGTON NC',
'wfoCity': 'WILMINGTON',
'state': 'NORTH CAROLINA',
},
'ILN': {
'region': 'ER',
'fullStationID': 'KILN',
'wfoCityState': 'WILMINGTON OH',
'wfoCity': 'WILMINGTON',
'state': 'OHIO',
},
'ILX': {
'region': 'CR',
'fullStationID': 'KILX',
'wfoCityState': 'LINCOLN IL',
'wfoCity': 'LINCOLN',
'state': 'ILLINOIS',
},
'IND': {
'region': 'CR',
'fullStationID': 'KIND',
'wfoCityState': 'INDIANAPOLIS IN',
'wfoCity': 'INDIANAPOLIS',
'state': 'INDIANA',
},
'IWX': {
'region': 'CR',
'fullStationID': 'KIWX',
'wfoCityState': 'NORTHERN INDIANA',
'wfoCity': 'NORTHERN INDIANA',
'state': 'INDIANA',
},
'JAN': {
'region': 'SR',
'fullStationID': 'KJAN',
'wfoCityState': 'JACKSON MS',
'wfoCity': 'JACKSON',
'state': 'MISSISSIPPI',
},
'JAX': {
'region': 'SR',
'fullStationID': 'KJAX',
'wfoCityState': 'JACKSONVILLE FL',
'wfoCity': 'JACKSONVILLE',
'state': 'FLORIDA',
},
'JKL': {
'region': 'CR',
'fullStationID': 'KJKL',
'wfoCityState': 'JACKSON KY',
'wfoCity': 'JACKSON',
'state': 'KENTUCKY',
},
'KEY': {
'region': 'SR',
'fullStationID': 'KKEY',
'wfoCityState': 'KEY WEST FL',
'wfoCity': 'KEY WEST',
'state': 'FLORIDA',
},
'LBF': {
'region': 'CR',
'fullStationID': 'KLBF',
'wfoCityState': 'NORTH PLATTE NE',
'wfoCity': 'NORTH PLATTE',
'state': 'NEBRASKA',
},
'LCH': {
'region': 'SR',
'fullStationID': 'KLCH',
'wfoCityState': 'LAKE CHARLES LA',
'wfoCity': 'LAKE CHARLES',
'state': 'LOUISIANA',
},
'LIX': {
'region': 'SR',
'fullStationID': 'KLIX',
'wfoCityState': 'NEW ORLEANS LA',
'wfoCity': 'NEW ORLEANS',
'state': 'LOUISIANA',
},
'LKN': {
'region': 'WR',
'fullStationID': 'KLKN',
'wfoCityState': 'ELKO NV',
'wfoCity': 'ELKO',
'state': 'NEVADA',
},
'LMK': {
'region': 'CR',
'fullStationID': 'KLMK',
'wfoCityState': 'LOUISVILLE KY',
'wfoCity': 'LOUISVILLE',
'state': 'KENTUCKY',
},
'LOT': {
'region': 'CR',
'fullStationID': 'KLOT',
'wfoCityState': 'CHICAGO IL',
'wfoCity': 'CHICAGO',
'state': 'ILLINOIS',
},
'LOX': {
'region': 'WR',
'fullStationID': 'KLOX',
'wfoCityState': 'LOS ANGELES/OXNARD CA',
'wfoCity': 'LOS ANGELES/OXNARD',
'state': 'CALIFORNIA',
},
'LSX': {
'region': 'CR',
'fullStationID': 'KLSX',
'wfoCityState': 'ST LOUIS MO',
'wfoCity': 'ST LOUIS',
'state': 'MISSOURI',
},
'LUB': {
'region': 'SR',
'fullStationID': 'KLUB',
'wfoCityState': 'LUBBOCK TX',
'wfoCity': 'LUBBOCK',
'state': 'TEXAS',
},
'LWX': {
'region': 'ER',
'fullStationID': 'KLWX',
'wfoCityState': 'BALTIMORE MD/WASHINGTON DC',
'wfoCity': 'BALTIMORE MD/WASHINGTON',
'state': 'WASHINGTON DC',
},
'LZK': {
'region': 'SR',
'fullStationID': 'KLZK',
'wfoCityState': 'LITTLE ROCK AR',
'wfoCity': 'LITTLE ROCK',
'state': 'ARKANSAS',
},
'MAF': {
'region': 'SR',
'fullStationID': 'KMAF',
'wfoCityState': 'MIDLAND/ODESSA TX',
'wfoCity': 'MIDLAND/ODESSA',
'state': 'TEXAS',
},
'MEG': {
'region': 'SR',
'fullStationID': 'KMEG',
'wfoCityState': 'MEMPHIS TN',
'wfoCity': 'MEMPHIS',
'state': 'TENNESSEE',
},
'MFL': {
'region': 'SR',
'fullStationID': 'KMFL',
'wfoCityState': 'MIAMI FL',
'wfoCity': 'MIAMI',
'state': 'FLORIDA',
},
'MFR': {
'region': 'WR',
'fullStationID': 'KMFR',
'wfoCityState': 'MEDFORD OR',
'wfoCity': 'MEDFORD',
'state': 'OREGON',
},
'MHX': {
'region': 'ER',
'fullStationID': 'KMHX',
'wfoCityState': 'NEWPORT/MOREHEAD CITY NC',
'wfoCity': 'NEWPORT/MOREHEAD CITY',
'state': 'NORTH CAROLINA',
},
'MKX': {
'region': 'CR',
'fullStationID': 'KMKX',
'wfoCityState': 'MILWAUKEE/SULLIVAN WI',
'wfoCity': 'MILWAUKEE/SULLIVAN',
'state': 'WISCONSIN',
},
'MLB': {
'region': 'SR',
'fullStationID': 'KMLB',
'wfoCityState': 'MELBOURNE FL',
'wfoCity': 'MELBOURNE',
'state': 'FLORIDA',
},
'MOB': {
'region': 'SR',
'fullStationID': 'KMOB',
'wfoCityState': 'MOBILE AL',
'wfoCity': 'MOBILE',
'state': 'ALABAMA',
},
'MPX': {
'region': 'CR',
'fullStationID': 'KMPX',
'wfoCityState': 'TWIN CITIES/CHANHASSEN MN',
'wfoCity': 'TWIN CITIES/CHANHASSEN',
'state': 'MINNESOTA',
},
'MQT': {
'region': 'CR',
'fullStationID': 'KMQT',
'wfoCityState': 'MARQUETTE MI',
'wfoCity': 'MARQUETTE',
'state': 'MICHIGAN',
},
'MRX': {
'region': 'SR',
'fullStationID': 'KMRX',
'wfoCityState': 'MORRISTOWN TN',
'wfoCity': 'MORRISTOWN',
'state': 'TENNESSEE',
},
'MSO': {
'region': 'WR',
'fullStationID': 'KMSO',
'wfoCityState': 'MISSOULA MT',
'wfoCity': 'MISSOULA',
'state': 'MONTANA',
},
'MTR': {
'region': 'WR',
'fullStationID': 'KMTR',
'wfoCityState': 'SAN FRANCISCO CA',
'wfoCity': 'SAN FRANCISCO',
'state': 'CALIFORNIA',
},
'NH1': {
'region': 'NC',
'fullStationID': 'KNHC',
'wfoCityState': 'NATIONAL HURRICANE CENTER MIAMI FL',
'wfoCity': 'MIAMI',
'state': '',
},
'NH2': {
'region': 'NC',
'fullStationID': 'KNHC',
'wfoCityState': 'NATIONAL HURRICANE CENTER MIAMI FL',
'wfoCity': 'MIAMI',
'state': '',
},
'OAX': {
'region': 'CR',
'fullStationID': 'KOAX',
'wfoCityState': 'OMAHA/VALLEY NE',
'wfoCity': 'OMAHA/VALLEY',
'state': 'NEBRASKA',
},
'OHX': {
'region': 'SR',
'fullStationID': 'KOHX',
'wfoCityState': 'NASHVILLE TN',
'wfoCity': 'NASHVILLE',
'state': 'TENNESSEE',
},
'OKX': {
'region': 'ER',
'fullStationID': 'KOKX',
'wfoCityState': 'UPTON NY',
'wfoCity': 'UPTON',
'state': 'NEW YORK',
},
'ONA': {
'region': 'NC',
'fullStationID': 'KWBC',
'wfoCityState': 'OCEAN PREDICTION CENTER WASHINGTON DC',
'wfoCity': 'WASHINGTON DC',
'state': '',
},
'ONP': {
'region': 'NC',
'fullStationID': 'KWBC',
'wfoCityState': 'OCEAN PREDICTION CENTER WASHINGTON DC',
'wfoCity': 'WASHINGTON DC',
'state': '',
},
'OTX': {
'region': 'WR',
'fullStationID': 'KOTX',
'wfoCityState': 'SPOKANE WA',
'wfoCity': 'SPOKANE',
'state': 'WASHINGTON',
},
'OUN': {
'region': 'SR',
'fullStationID': 'KOUN',
'wfoCityState': 'NORMAN OK',
'wfoCity': 'NORMAN',
'state': 'OKLAHOMA',
},
'PAH': {
'region': 'CR',
'fullStationID': 'KPAH',
'wfoCityState': 'PADUCAH KY',
'wfoCity': 'PADUCAH',
'state': 'KENTUCKY',
},
'PBZ': {
'region': 'ER',
'fullStationID': 'KPBZ',
'wfoCityState': 'PITTSBURGH PA',
'wfoCity': 'PITTSBURGH',
'state': 'PENNSYLVANIA',
},
'PDT': {
'region': 'WR',
'fullStationID': 'KPDT',
'wfoCityState': 'PENDLETON OR',
'wfoCity': 'PENDLETON',
'state': 'OREGON',
},
'PHI': {
'region': 'ER',
'fullStationID': 'KPHI',
'wfoCityState': 'MOUNT HOLLY NJ',
'wfoCity': 'MOUNT HOLLY',
'state': 'NEW JERSEY',
},
'PIH': {
'region': 'WR',
'fullStationID': 'KPIH',
'wfoCityState': 'POCATELLO ID',
'wfoCity': 'POCATELLO',
'state': 'IDAHO',
},
'PQR': {
'region': 'WR',
'fullStationID': 'KPQR',
'wfoCityState': 'PORTLAND OR',
'wfoCity': 'PORTLAND',
'state': 'OREGON',
},
'PSR': {
'region': 'WR',
'fullStationID': 'KPSR',
'wfoCityState': 'PHOENIX AZ',
'wfoCity': 'PHOENIX',
'state': 'ARIZONA',
},
'PUB': {
'region': 'CR',
'fullStationID': 'KPUB',
'wfoCityState': 'PUEBLO CO',
'wfoCity': 'PUEBLO',
'state': 'COLORADO',
},
'RAH': {
'region': 'ER',
'fullStationID': 'KRAH',
'wfoCityState': 'RALEIGH NC',
'wfoCity': 'RALEIGH',
'state': 'NORTH CAROLINA',
},
'REV': {
'region': 'WR',
'fullStationID': 'KREV',
'wfoCityState': 'RENO NV',
'wfoCity': 'RENO',
'state': 'NEVADA',
},
'RIW': {
'region': 'CR',
'fullStationID': 'KRIW',
'wfoCityState': 'RIVERTON WY',
'wfoCity': 'RIVERTON',
'state': 'WYOMING',
},
'RLX': {
'region': 'ER',
'fullStationID': 'KRLX',
'wfoCityState': 'CHARLESTON WV',
'wfoCity': 'CHARLESTON',
'state': 'WEST VIRGINIA',
},
'RNK': {
'region': 'ER',
'fullStationID': 'KRNK',
'wfoCityState': 'BLACKSBURG VA',
'wfoCity': 'BLACKSBURG',
'state': 'VIRGINIA',
},
'SEW': {
'region': 'WR',
'fullStationID': 'KSEW',
'wfoCityState': 'SEATTLE WA',
'wfoCity': 'SEATTLE',
'state': 'WASHINGTON',
},
'SGF': {
'region': 'CR',
'fullStationID': 'KSGF',
'wfoCityState': 'SPRINGFIELD MO',
'wfoCity': 'SPRINGFIELD',
'state': 'MISSOURI',
},
'SGX': {
'region': 'WR',
'fullStationID': 'KSGX',
'wfoCityState': 'SAN DIEGO CA',
'wfoCity': 'SAN DIEGO',
'state': 'CALIFORNIA',
},
'SHV': {
'region': 'SR',
'fullStationID': 'KSHV',
'wfoCityState': 'SHREVEPORT LA',
'wfoCity': 'SHREVEPORT',
'state': 'LOUISIANA',
},
'SJT': {
'region': 'SR',
'fullStationID': 'KSJT',
'wfoCityState': 'SAN ANGELO TX',
'wfoCity': 'SAN ANGELO',
'state': 'TEXAS',
},
'SJU': {
'region': 'SR',
'fullStationID': 'TJSJ',
'wfoCityState': 'SAN JUAN PR',
'wfoCity': 'SAN JUAN',
'state': 'PUERTO RICO',
},
'SLC': {
'region': 'WR',
'fullStationID': 'KSLC',
'wfoCityState': 'SALT LAKE CITY UT',
'wfoCity': 'SALT LAKE CITY',
'state': 'UTAH',
},
'STO': {
'region': 'WR',
'fullStationID': 'KSTO',
'wfoCityState': 'SACRAMENTO CA',
'wfoCity': 'SACRAMENTO',
'state': 'CALIFORNIA',
},
'TAE': {
'region': 'SR',
'fullStationID': 'KTAE',
'wfoCityState': 'TALLAHASSEE FL',
'wfoCity': 'TALLAHASSEE',
'state': 'FLORIDA',
},
'TBW': {
'region': 'SR',
'fullStationID': 'KTBW',
'wfoCityState': 'TAMPA BAY RUSKIN FL',
'wfoCity': 'TAMPA BAY RUSKIN',
'state': 'FLORIDA',
},
'TFX': {
'region': 'WR',
'fullStationID': 'KTFX',
'wfoCityState': 'GREAT FALLS MT',
'wfoCity': 'GREAT FALLS',
'state': 'MONTANA',
},
'TOP': {
'region': 'CR',
'fullStationID': 'KTOP',
'wfoCityState': 'TOPEKA KS',
'wfoCity': 'TOPEKA',
'state': 'KANSAS',
},
'TSA': {
'region': 'SR',
'fullStationID': 'KTSA',
'wfoCityState': 'TULSA OK',
'wfoCity': 'TULSA',
'state': 'OKLAHOMA',
},
'TWC': {
'region': 'WR',
'fullStationID': 'KTWC',
'wfoCityState': 'TUCSON AZ',
'wfoCity': 'TUCSON',
'state': 'ARIZONA',
},
'UNR': {
'region': 'CR',
'fullStationID': 'KUNR',
'wfoCityState': 'RAPID CITY SD',
'wfoCity': 'RAPID CITY',
'state': 'SOUTH DAKOTA',
},
'VEF': {
'region': 'WR',
'fullStationID': 'KVEF',
'wfoCityState': 'LAS VEGAS NV',
'wfoCity': 'LAS VEGAS',
'state': 'NEVADA',
},
}

View file

@ -1,90 +0,0 @@
; Logging Configuration
; To enable file logging and modify the log format see the appropriate sections
; below.
;
; For a more detailed description of this configuration format see the logging
; module documentation in the Python Library Reference 14.5.10.2
; ---- Section Declarations ---------------------------------------------------
[loggers]
keys=root
[handlers]
keys=console,file
[formatters]
keys=console,file
; ---- Loggers ----------------------------------------------------------------
[logger_root]
level=DEBUG
handlers=console,file
; ---- Handlers ---------------------------------------------------------------
[handler_console]
class=StreamHandler
level=INFO
formatter=console
args=(sys.stdout,)
[handler_file]
class=StreamHandler
formatter=console
level=CRITICAL
args=(sys.stderr,)
; ---- Enable File Logging ----------------------------------------------------
;
; Uncomment the following lines to enable file logging. The previous lines can
; remain uncommented as the following will simply override the values.
;
; args replace 'program.log' with desired filename
; replace 'w' with 'a' to append to the log file
;class=FileHandler
;level=DEBUG
;formatter=file
;args=('program.log','a')
; ---- Formatters -------------------------------------------------------------
; Configure the format of the console and file log
;
; %(name)s Name of the logger (logging channel).
; %(levelno)s Numeric logging level for the message
; (DEBUG, INFO, WARNING, ERROR, CRITICAL).
; %(levelname)s Text logging level for the message
; ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL').
; %(pathname)s Full pathname of the source file where the logging call was
; issued (if available).
; %(filename)s Filename portion of pathname.
; %(module)s Module (name portion of filename).
; %(funcName)s Name of function containing the logging call.
; %(lineno)d Source line number where the logging call was issued
; (if available).
; %(created)f Time when the LogRecord was created
; (as returned by time.time()).
; %(relativeCreated)d
; Time in milliseconds when the LogRecord was created,
; relative to the time the logging module was loaded.
; %(asctime)s Human-readable time when the LogRecord was created. By default
; this is of the form ``2003-07-08 16:49:45,896'' (the numbers
; after the comma are millisecond portion of the time).
; %(msecs)d Millisecond portion of the time when the LogRecord was created.
; %(thread)d Thread ID (if available).
; %(threadName)s
; Thread name (if available).
; %(process)d Process ID (if available).
; %(message)s The logged message, computed as msg % args.
[formatter_file]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=
[formatter_console]
format=%(name)s - %(levelname)s - %(message)s
datefmt=

View file

@ -17,6 +17,14 @@
# See the AWIPS II Master Rights File ("Master Rights File.pdf") for
# further licensing information.
##
#
# SOFTWARE HISTORY
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# Oct 20, 2014 #3685 randerso Changed to support mixed case
#
##
#-------------------------------------------------------------------------
# File Name: AFD.py
# Description: This product creates a Area Forecast Discussion product.
@ -245,7 +253,7 @@ class TextProduct(TextRules.TextRules, SampleAnalysis.SampleAnalysis):
],
"popStartZ_AM": 12, #hour UTC
"WWA_Nil" : "NONE.",
"WWA_Nil" : "None.",
"hazardSamplingThreshold": (10, None), #(%cov, #points)
}
@ -621,12 +629,13 @@ class TextProduct(TextRules.TextRules, SampleAnalysis.SampleAnalysis):
productName = self.checkTestMode(argDict, self._productName)
fcst = fcst + self._pil + "\n\n"
fcst = fcst + productName + "\n"
fcst = fcst + "NATIONAL WEATHER SERVICE "
fcst = fcst + self._wfoCityState +"\n"
fcst = fcst + issuedByString
fcst = fcst + self._timeLabel + "\n\n"
s = self._pil + "\n\n" + \
productName + "\n" + \
"NATIONAL WEATHER SERVICE " + \
self._wfoCityState +"\n" + \
issuedByString + \
self._timeLabel + "\n\n"
fcst = fcst + s.upper()
return fcst
####################################################################
@ -776,7 +785,7 @@ class TextProduct(TextRules.TextRules, SampleAnalysis.SampleAnalysis):
# If no hazards are found, append the null phrase
if len(stateHazardList) == 0:
fcst = fcst + "NONE.\n"
fcst = fcst + self._WWA_Nil + "\n"
continue
# If hazards are found, then build the hazard phrases
@ -822,7 +831,7 @@ class TextProduct(TextRules.TextRules, SampleAnalysis.SampleAnalysis):
idString = self.makeUGCString(ids)
# hazard phrase
phrase = hazName + ' ' + timing + ' FOR ' + idString + '.'
phrase = hazName + ' ' + timing + ' for ' + idString + '.'
# Indent if there is a state list associated
if len(self._state_IDs) > 1:

View file

@ -27,6 +27,15 @@
#
# Author: davis
# ----------------------------------------------------------------------------
##
#
# SOFTWARE HISTORY
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# Oct 20, 2014 #3685 randerso Changed to support mixed case
#
##
#-------------------------------------------------------------------------
# Example Output:
# Refer to the NWS 10-518 Directive for further information.
@ -111,7 +120,6 @@ class TextProduct(CivilEmerg.TextProduct):
return fcst
def _postProcessProduct(self, fcst, argDict):
fcst = string.upper(fcst)
fcst = self.endline(fcst, linelength=self._lineLength, breakStr=[" ", "...", "-"])
self.setProgressPercentage(100)
self.progressMessage(0, 100, self._displayName + " Complete")

View file

@ -27,6 +27,15 @@
#
# Author: Matt Davis
# ----------------------------------------------------------------------------
##
#
# SOFTWARE HISTORY
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# Oct 20, 2014 #3685 randerso Changed to support mixed case
#
##
#-------------------------------------------------------------------------
# Example Output:
# Refer to the NWS 10-922 Directive for further information.
@ -119,10 +128,10 @@ class TextProduct(GenericReport.TextProduct):
issuedByString = self.getIssuedByString()
productName = self.checkTestMode(argDict, self._productName)
fcst = fcst + productName + "\n" + \
s = productName + "\n" + \
"NATIONAL WEATHER SERVICE " + self._wfoCityState + \
"\n" + issuedByString + self._timeLabel + "\n\n"
fcst = string.upper(fcst)
fcst = fcst + s.upper()
return fcst
def _makeProduct(self, fcst, editArea, areaLabel, argDict):
@ -132,8 +141,6 @@ class TextProduct(GenericReport.TextProduct):
return fcst
def _postProcessProduct(self, fcst, argDict):
fcst = string.upper(fcst)
#
# Clean up multiple line feeds
#

View file

@ -105,6 +105,13 @@
# Example Output:
# Refer to the NWS C11 and 10-503 Directives for Public Weather Services.
#-------------------------------------------------------------------------
#
# SOFTWARE HISTORY
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# Oct 20, 2014 #3685 randerso Removed upper case conversions
#
##
import TextRules
import SampleAnalysis
@ -200,7 +207,6 @@ class TextProduct(TextRules.TextRules, SampleAnalysis.SampleAnalysis):
fcst = self._postProcessArea(fcst, editArea, areaLabel, argDict)
fraction = fractionOne
fcst = self._postProcessProduct(fcst, argDict)
fcst = string.upper(fcst)
return fcst
def _getVariables(self, argDict):
@ -252,13 +258,13 @@ class TextProduct(TextRules.TextRules, SampleAnalysis.SampleAnalysis):
issuedByString = self.getIssuedByString()
productName = self.checkTestMode(argDict, productName)
fcst = fcst + self._wmoID + " " + self._fullStationID + " " + \
s = self._wmoID + " " + self._fullStationID + " " + \
self._ddhhmmTime + "\n" + self._pil + "\n\n" +\
productName + "\n" +\
"NATIONAL WEATHER SERVICE " + self._wfoCityState + \
"\n" + issuedByString + self._timeLabel + "\n\n"
fcst = string.upper(fcst)
fcst = fcst + s.upper()
return fcst
def _preProcessArea(self, fcst, editArea, areaLabel, argDict):
@ -278,7 +284,6 @@ class TextProduct(TextRules.TextRules, SampleAnalysis.SampleAnalysis):
return fcst + "\n\n$$\n"
def _postProcessProduct(self, fcst, argDict):
fcst = string.upper(fcst)
self.setProgressPercentage(100)
self.progressMessage(0, 100, self._displayName + " Complete")
return fcst

View file

@ -20,8 +20,13 @@
########################################################################
# Hazard_AQA.py
#
# SOFTWARE HISTORY
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# Oct 20, 2014 #3685 randerso Changed to support mixed case
#
##
##########################################################################
import GenericHazards
import string, time, re, os, types, copy
import ProcessVariableList
@ -157,10 +162,10 @@ class TextProduct(GenericHazards.TextProduct):
# Placeholder for Agency Names to be filled in in _postProcessProduct
#fcst = fcst + "@AGENCYNAMES" + "\n"
fcst = fcst + "RELAYED BY NATIONAL WEATHER SERVICE " + self._wfoCityState + "\n" +\
s = "RELAYED BY NATIONAL WEATHER SERVICE " + self._wfoCityState + "\n" +\
issuedByString + self._timeLabel + "\n\n"
fcst = string.upper(fcst)
fcst = fcst + s.upper()
return fcst
def headlinesTiming(self, tree, node, key, timeRange, areaLabel, issuanceTime):
@ -301,8 +306,6 @@ class TextProduct(GenericHazards.TextProduct):
fixMultiLF = re.compile(r'(\n\n)\n*', re.DOTALL)
fcst = fixMultiLF.sub(r'\1', fcst)
## # Keep body in lower case, if desired
## fcst = string.upper(fcst)
self.setProgressPercentage(100)
self.progressMessage(0, 100, self._displayName + " Complete")
return fcst

View file

@ -22,6 +22,15 @@
#
##
##########################################################################
##
#
# SOFTWARE HISTORY
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# Oct 20, 2014 #3685 randerso Changed to support mixed case
#
##
import GenericHazards
import string, time, re, os, types, copy, sets
import ModuleAccessor, LogStream
@ -108,12 +117,12 @@ class TextProduct(GenericHazards.TextProduct):
productName = self.checkTestMode(argDict,
self._productName + watchPhrase)
fcst = fcst + self._wmoID + " " + self._fullStationID + " " + \
s = self._wmoID + " " + self._fullStationID + " " + \
self._ddhhmmTime + "\n" + self._pil + "\n\n" +\
productName + "\n" +\
"NATIONAL WEATHER SERVICE " + self._wfoCityState + \
"\n" + issuedByString + self._timeLabel + "\n" + self._easPhrase + "\n"
fcst = string.upper(fcst)
fcst = fcst + s.upper()
return fcst

View file

@ -27,6 +27,14 @@
#
# Author: Matt Davis
# ----------------------------------------------------------------------------
##
#
# SOFTWARE HISTORY
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# Oct 20, 2014 #3685 randerso Changed to support mixed case
#
##
import GenericReport
@ -67,7 +75,7 @@ class TextProduct(GenericReport.TextProduct):
"language": "english",
"lineLength": 66, #Maximum line length
"includeCities" : 0, # Cities included in area header
"cityDescriptor" : "INCLUDING THE CITIES OF",
"cityDescriptor" : "Including the cities of",
"includeZoneNames" : 0, # Zone names will be included in the area header
"includeIssueTime" : 0, # This should be set to zero
"singleComboOnly" : 1, # Used for non-segmented products
@ -108,20 +116,18 @@ class TextProduct(GenericReport.TextProduct):
issuedByString = self.getIssuedByString()
productName = self.checkTestMode(argDict, self._productName)
fcst = fcst + productName + "\n" + \
s = productName + "\n" + \
"NATIONAL WEATHER SERVICE " + self._wfoCityState + \
"\n" + issuedByString + self._timeLabel + "\n\n"
fcst = string.upper(fcst)
fcst = fcst + s.upper()
return fcst
def _makeProduct(self, fcst, editArea, areaLabel, argDict):
fcst = fcst + "...PUBLIC INFORMATION STATEMENT...\n\n"
fcst = fcst + "|* INFORMATION GOES HERE *|\n\n"
fcst = fcst + "|* Information goes here *|\n\n"
return fcst
def _postProcessProduct(self, fcst, argDict):
fcst = string.upper(fcst)
#
# Clean up multiple line feeds
#

View file

@ -27,6 +27,15 @@
#
# Author: Matt Davis
# ----------------------------------------------------------------------------
##
#
# SOFTWARE HISTORY
# Date Ticket# Engineer Description
# ------------ ---------- ----------- --------------------------
# Oct 20, 2014 #3685 randerso Changed to support mixed case
#
##
#-------------------------------------------------------------------------
# Example Output:
# Refer to the NWS 10-518 Directive for further information.
@ -111,10 +120,10 @@ class TextProduct(GenericReport.TextProduct):
issuedByString = self.getIssuedByString()
productName = self.checkTestMode(argDict, self._productName)
fcst = fcst + productName + "\n" + \
s = productName + "\n" + \
"NATIONAL WEATHER SERVICE " + self._wfoCityState + \
"\n" + issuedByString + self._timeLabel + "\n\n"
fcst = string.upper(fcst)
fcst = fcst + s.upper()
return fcst
def _makeProduct(self, fcst, editArea, areaLabel, argDict):
@ -123,8 +132,6 @@ class TextProduct(GenericReport.TextProduct):
return fcst
def _postProcessProduct(self, fcst, argDict):
fcst = string.upper(fcst)
#
# Clean up multiple line feeds
#

View file

@ -0,0 +1,154 @@
/**
* This software was developed and / or modified by Raytheon Company,
* pursuant to Contract DG133W-05-CQ-1067 with the US Government.
*
* U.S. EXPORT CONTROLLED TECHNICAL DATA
* This software product contains export-restricted data whose
* export/transfer/disclosure is restricted by U.S. law. Dissemination
* to non-U.S. persons whether in the United States or abroad requires
* an export license or other authorization.
*
* Contractor Name: Raytheon Company
* Contractor Address: 6825 Pine Street, Suite 340
* Mail Stop B8
* Omaha, NE 68106
* 402.291.0100
*
* See the AWIPS II Master Rights File ("Master Rights File.pdf") for
* further licensing information.
**/
package com.raytheon.uf.common.dataplugin.text.db;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.raytheon.uf.common.localization.FileUpdatedMessage;
import com.raytheon.uf.common.localization.ILocalizationFileObserver;
import com.raytheon.uf.common.localization.IPathManager;
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationLevel;
import com.raytheon.uf.common.localization.LocalizationContext.LocalizationType;
import com.raytheon.uf.common.localization.LocalizationFile;
import com.raytheon.uf.common.localization.PathManagerFactory;
import com.raytheon.uf.common.localization.exception.LocalizationException;
import com.raytheon.uf.common.status.IUFStatusHandler;
import com.raytheon.uf.common.status.UFStatus;
import com.raytheon.uf.common.util.FileUtil;
/**
* Utilities to support mixed case product generation on a per Product ID (nnn)
* basis
*
* <pre>
*
* SOFTWARE HISTORY
*
* Date Ticket# Engineer Description
* ------------ ---------- ----------- --------------------------
* Oct 1, 2014 #3685 randerso Initial creation
*
* </pre>
*
* @author randerso
* @version 1.0
*/
public class MixedCaseProductSupport {
private static final IUFStatusHandler statusHandler = UFStatus
.getHandler(MixedCaseProductSupport.class);
private static final String MIXED_CASE_DIR = "mixedCase";
private static final String MIXED_CASE_PIDS_FILE = FileUtil.join(
MIXED_CASE_DIR, "mixedCaseProductIds.txt");
private static final char COMMENT_DELIMITER = '#';
private static Set<String> mixedCasePids;
private static IPathManager pm = PathManagerFactory.getPathManager();
private static LocalizationFile baseDir;
public static Set<String> getMixedCasePids() {
// setup up the file updated observer
synchronized (MixedCaseProductSupport.class) {
if (baseDir == null) {
baseDir = pm.getLocalizationFile(
pm.getContext(LocalizationType.COMMON_STATIC,
LocalizationLevel.BASE), MIXED_CASE_DIR);
baseDir.addFileUpdatedObserver(new ILocalizationFileObserver() {
@Override
public void fileUpdated(FileUpdatedMessage message) {
mixedCasePids = null;
}
});
}
}
synchronized (MixedCaseProductSupport.class) {
if (mixedCasePids == null) {
// get all localization files in the hierarchy and merge them.
Map<LocalizationLevel, LocalizationFile> fileHierarchy = pm
.getTieredLocalizationFile(
LocalizationType.COMMON_STATIC,
MIXED_CASE_PIDS_FILE);
Set<String> newPids = new HashSet<String>();
for (LocalizationFile lf : fileHierarchy.values()) {
String filePath = lf.getFile().getAbsolutePath();
try (BufferedReader in = new BufferedReader(
new InputStreamReader(lf.openInputStream()))) {
String line;
while ((line = in.readLine()) != null) {
int pos = line.indexOf(COMMENT_DELIMITER);
if (pos >= 0) {
line = line.substring(0, pos);
}
line = line.trim().toUpperCase();
String[] pids = line.split("[\\s,]+");
for (String pid : pids) {
if (pid.length() == 3) {
newPids.add(pid);
} else if (pid.isEmpty()) {
continue;
} else {
statusHandler.warn("Invalid Product ID \""
+ pid + "\" found in " + filePath
+ ", ignored.");
}
}
}
mixedCasePids = newPids;
} catch (IOException e) {
statusHandler.error("Error reading " + filePath, e);
} catch (LocalizationException e) {
statusHandler.error("Error retrieving " + filePath, e);
}
}
mixedCasePids = newPids;
}
}
return mixedCasePids;
}
public static boolean isMixedCase(String pid) {
return getMixedCasePids().contains(pid.toUpperCase());
}
public static String conditionalToUpper(String pid, String text) {
if (!isMixedCase(pid)) {
text = text.toUpperCase();
}
return text;
}
}

View file

@ -0,0 +1,5 @@
# This file contains the product IDs (nnn) that should be sent in mixed case.
# Product IDs should be 3 characters long and delimited by commas or white space.
# Overrides to the base file will add to the list of mixed case products
AFD PNS RWS PWO TCD TWD TWO WRK # Phase 1 Products

View file

@ -94,6 +94,8 @@
<permission id="com.raytheon.localization.site/common_static/archiver/purger"/>
<permission id="com.raytheon.localization.site/common_static/archiver/purger/retention"/>
<permission id="com.raytheon.localization.site/common_static/archiver/purger/case"/>
<permission id="com.raytheon.localization.site/common_static/mixedCase"/>
<user userId="ALL">
<userPermission>com.raytheon.localization.site/common_static/purge</userPermission>