TreeLine/ 0000755 0001750 0001750 00000000000 14307147630 011250 5 ustar doug doug TreeLine/samples/ 0000755 0001750 0001750 00000000000 13262465526 012722 5 ustar doug doug TreeLine/samples/210en_sample_char_format.trln 0000644 0001750 0001750 00000005740 13262465526 020364 0 ustar doug doug {
"formats": [
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formatname": "ROOT",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "TOPIC",
"fieldtype": "Text"
},
{
"fieldname": "TEXT",
"fieldtype": "Text",
"lines": 7
}
],
"formathtml": true,
"formatname": "TEXT_PARA",
"icon": "doc",
"outputlines": [
"{*TOPIC*}",
"{*TEXT*}"
],
"titleline": "{*TOPIC*}"
}
],
"nodes": [
{
"children": [
"14e55e4895a411e79cb17054d2175f18",
"14e5604695a411e79cb17054d2175f18",
"14e5616895a411e79cb17054d2175f18",
"14e5626c95a411e79cb17054d2175f18",
"14e5635c95a411e79cb17054d2175f18"
],
"data": {
"Name": "Character Format Examples"
},
"format": "ROOT",
"uid": "14e559fc95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"TEXT": "All titles can be set to bold by adding <b>...</b> tags in the Configure Data Types dialog's Output tab. The \"Allow HTML rich text in format\" option must also be checked in the Type Config tab.",
"TOPIC": "Bold Titles"
},
"format": "TEXT_PARA",
"uid": "14e55e4895a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"TEXT": "Font formatting commands can be found in the Edit menu and in the right-click context menu in the \"Data Editor\" view.",
"TOPIC": "Bold Text"
},
"format": "TEXT_PARA",
"uid": "14e5604695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"TEXT": "Text can also be set to various colors.",
"TOPIC": "Colored Text"
},
"format": "TEXT_PARA",
"uid": "14e5616895a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"TEXT": "Large text and small text",
"TOPIC": "Font Size"
},
"format": "TEXT_PARA",
"uid": "14e5626c95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"TEXT": "Text in italics and even text underlined",
"TOPIC": "Italics and Underline"
},
"format": "TEXT_PARA",
"uid": "14e5635c95a411e79cb17054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"14e559fc95a411e79cb17054d2175f18"
]
}
} TreeLine/samples/320en_sample_other_fields.trln 0000644 0001750 0001750 00000010664 13262465526 020551 0 ustar doug doug {
"formats": [
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "DEFAULT",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"childtype": "DEFAULT",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "FILE_INFO",
"outputlines": [
"{*Name*}",
"File: {*!File_Path*}/{*!File_Name*}",
"File Size: {*!File_Size*} bytes",
"File Modified: {*!File_Mod_Date*} {*!File_Mod_Time*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "File_Name",
"fieldtype": "Text"
},
{
"fieldname": "File_Path",
"fieldtype": "Text"
},
{
"fieldname": "File_Size",
"fieldtype": "Number",
"format": "#"
},
{
"fieldname": "File_Mod_Date",
"fieldtype": "Date",
"format": "%B %-d, %Y"
},
{
"fieldname": "File_Mod_Time",
"fieldtype": "Time",
"format": "%-I:%M:%S %p"
},
{
"fieldname": "File_Owner",
"fieldtype": "Text"
},
{
"fieldname": "Page_Number",
"fieldtype": "Text"
},
{
"fieldname": "Number_of_Pages",
"fieldtype": "Text"
}
],
"formatname": "INT_TL_FILE_DATA_FORM",
"outputlines": [
""
],
"titleline": ""
},
{
"childtype": "DEFAULT",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "PARENT_CHILD",
"outputlines": [
"{*Name*}",
"Parent's Name: {**Name*}",
"Children's Names: {*&Name*}"
],
"titleline": "{*Name*}"
},
{
"childtype": "DEFAULT",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "ROOT",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
}
],
"nodes": [
{
"children": [
"50cdc8dc95a411e79cb17054d2175f18",
"50cdce0495a411e79cb17054d2175f18",
"50cdcf7695a411e79cb17054d2175f18"
],
"data": {
"Name": "Other Field References"
},
"format": "ROOT",
"uid": "50cdc36e95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "File Information"
},
"format": "FILE_INFO",
"uid": "50cdc8dc95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Parent Info"
},
"format": "PARENT_CHILD",
"uid": "50cdce0495a411e79cb17054d2175f18"
},
{
"children": [
"50cdd27895a411e79cb17054d2175f18",
"50cdd3e095a411e79cb17054d2175f18",
"50cdd4e495a411e79cb17054d2175f18"
],
"data": {
"Name": "Child Info"
},
"format": "PARENT_CHILD",
"uid": "50cdcf7695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "First child"
},
"format": "DEFAULT",
"uid": "50cdd27895a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Next child"
},
"format": "DEFAULT",
"uid": "50cdd3e095a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Third child"
},
"format": "DEFAULT",
"uid": "50cdd4e495a411e79cb17054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"50cdc36e95a411e79cb17054d2175f18"
]
}
} TreeLine/samples/240en_sample_table_booklist.trln 0000644 0001750 0001750 00000006601 13262465526 021074 0 ustar doug doug {
"formats": [
{
"childtype": "BOOK",
"fields": [
{
"fieldname": "AuthorFirstName",
"fieldtype": "Text"
},
{
"fieldname": "AuthorLastName",
"fieldtype": "Text"
},
{
"fieldname": "WebSite",
"fieldtype": "ExternalLink",
"prefix": "<",
"suffix": ">"
}
],
"formatname": "AUTHOR",
"icon": "book_1",
"outputlines": [
"{*AuthorFirstName*} {*AuthorLastName*} {*WebSite*}"
],
"titleline": "{*AuthorFirstName*} {*AuthorLastName*}"
},
{
"fields": [
{
"fieldname": "Title",
"fieldtype": "Text"
},
{
"fieldname": "Copyright",
"fieldtype": "Number",
"format": "0000"
},
{
"fieldname": "Own",
"fieldtype": "Boolean",
"format": "yes/no"
},
{
"fieldname": "ReadDate",
"fieldtype": "Date",
"format": "%-m/%-d/%Y"
},
{
"fieldname": "Rating",
"fieldtype": "Choice",
"format": "1/2/3/4/5"
},
{
"fieldname": "Plot",
"fieldtype": "Text",
"lines": 7
}
],
"formathtml": true,
"formatname": "BOOK",
"icon": "book_3",
"outputlines": [
"Title: \"{*Title*}\"",
"Status: (c) {*Copyright*}",
"Own: {*Own*}",
"Last Read: {*ReadDate*}",
"Rating: {*Rating*}",
"Plot: {*Plot*}"
],
"spacebetween": false,
"tables": true,
"titleline": "\"{*Title*}\""
},
{
"childtype": "AUTHOR",
"fields": [
{
"fieldname": "NAME",
"fieldtype": "Text"
}
],
"formatname": "ROOT",
"outputlines": [
"{*NAME*}"
],
"titleline": "{*NAME*}"
}
],
"nodes": [
{
"children": [
"5cd2946cfe1011e7b8dc7054d2175f18",
"5cd2bffafe1011e7b8dc7054d2175f18"
],
"data": {
"NAME": "SF Books"
},
"format": "ROOT",
"uid": "5cd28d1efe1011e7b8dc7054d2175f18"
},
{
"children": [
"5cd2b3d4fe1011e7b8dc7054d2175f18",
"5cd2bdc0fe1011e7b8dc7054d2175f18"
],
"data": {
"AuthorFirstName": "Greg",
"AuthorLastName": "Bear",
"WebSite": "www.gregbear.com"
},
"format": "AUTHOR",
"uid": "5cd2946cfe1011e7b8dc7054d2175f18"
},
{
"children": [],
"data": {
"Copyright": "1999",
"Own": "false",
"Plot": "Evolution caused by virus begining again",
"Rating": "4",
"ReadDate": "2000-10-01",
"Title": "Darwin's Radio"
},
"format": "BOOK",
"uid": "5cd2b3d4fe1011e7b8dc7054d2175f18"
},
{
"children": [],
"data": {
"Copyright": "1985",
"Own": "true",
"Plot": "Smart viruses take over",
"Rating": "2",
"ReadDate": "1998-07-01",
"Title": "Blood Music"
},
"format": "BOOK",
"uid": "5cd2bdc0fe1011e7b8dc7054d2175f18"
},
{
"children": [
"5cd2c1bcfe1011e7b8dc7054d2175f18",
"5cd2c392fe1011e7b8dc7054d2175f18",
"5cd2c54afe1011e7b8dc7054d2175f18"
],
"data": {
"AuthorFirstName": "Orson Scott",
"AuthorLastName": "Card",
"WebSite": "www.hatrack.com"
},
"format": "AUTHOR",
"uid": "5cd2bffafe1011e7b8dc7054d2175f18"
},
{
"children": [],
"data": {
"Copyright": "1996",
"Own": "Yes",
"Plot": "Time travel to change history; discovery of America",
"Rating": "4",
"ReadDate": "1998-09-01",
"Title": "Pastwatch, The Redemption of Christopher Columbus"
},
"format": "BOOK",
"uid": "5cd2c1bcfe1011e7b8dc7054d2175f18"
},
{
"children": [],
"data": {
"Copyright": "1999",
"Own": "Yes",
"Plot": "Boy travels back to Russian fairy tale",
"Rating": "5",
"ReadDate": "2000-08-01",
"Title": "Enchantment"
},
"format": "BOOK",
"uid": "5cd2c392fe1011e7b8dc7054d2175f18"
},
{
"children": [],
"data": {
"Copyright": "1999",
"Own": "Yes",
"Plot": "Ender's Game from Bean's perspective",
"Rating": "5",
"ReadDate": "2001-05-01",
"Title": "Ender's Shadow"
},
"format": "BOOK",
"uid": "5cd2c54afe1011e7b8dc7054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"5cd28d1efe1011e7b8dc7054d2175f18"
]
}
} TreeLine/samples/310en_sample_conditional_todo.trln 0000644 0001750 0001750 00000013751 13262465526 021431 0 ustar doug doug {
"formats": [
{
"childtype": "TASK_UNDONE",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "ROOT",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"condition": "Done == \"true\"",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Done",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "false"
},
{
"fieldname": "Urgent",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "false"
}
],
"formathtml": true,
"formatname": "TASK_DONE",
"icon": "smiley_4",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Done",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "false"
},
{
"fieldname": "Urgent",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "false"
}
],
"formathtml": true,
"formatname": "TASK_UNDONE",
"generic": "TASK_DONE",
"icon": "smiley_2",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"condition": "Done == \"false\" and Urgent == \"true\"",
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Done",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "false"
},
{
"fieldname": "Urgent",
"fieldtype": "Boolean",
"format": "yes/no",
"init": "true"
}
],
"formathtml": true,
"formatname": "TASK_UNDONE_URGENT",
"generic": "TASK_DONE",
"icon": "smiley_5",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}!!!"
}
],
"nodes": [
{
"children": [
"4295fc4e95a411e79cb17054d2175f18",
"42960f7c95a411e79cb17054d2175f18"
],
"data": {
"Name": "Conditional Task List"
},
"format": "ROOT",
"uid": "4295faaa95a411e79cb17054d2175f18"
},
{
"children": [
"4295ff6e95a411e79cb17054d2175f18",
"429602ac95a411e79cb17054d2175f18",
"429603c495a411e79cb17054d2175f18",
"429607b695a411e79cb17054d2175f18",
"42960cca95a411e79cb17054d2175f18"
],
"data": {
"Name": "Home Tasks"
},
"format": "ROOT",
"uid": "4295fc4e95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Mow lawn",
"Urgent": "false"
},
"format": "TASK_UNDONE",
"uid": "4295ff6e95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Patch wall",
"Urgent": "false"
},
"format": "TASK_UNDONE",
"uid": "429602ac95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Vacuum",
"Urgent": "false"
},
"format": "TASK_UNDONE",
"uid": "429603c495a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Walk dog",
"Urgent": "true"
},
"format": "TASK_UNDONE_URGENT",
"uid": "429607b695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "true",
"Name": "Watch TV",
"Urgent": "false"
},
"format": "TASK_DONE",
"uid": "42960cca95a411e79cb17054d2175f18"
},
{
"children": [
"4296107695a411e79cb17054d2175f18",
"429611b695a411e79cb17054d2175f18",
"429612a695a411e79cb17054d2175f18",
"4296139695a411e79cb17054d2175f18"
],
"data": {
"Name": "Work Tasks"
},
"format": "ROOT",
"uid": "42960f7c95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "true",
"Name": "Play solitaire",
"Urgent": "false"
},
"format": "TASK_DONE",
"uid": "4296107695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Write documents",
"Urgent": "false"
},
"format": "TASK_UNDONE",
"uid": "429611b695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "true",
"Name": "Eat lunch",
"Urgent": "false"
},
"format": "TASK_DONE",
"uid": "429612a695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Done": "false",
"Name": "Compliment boss",
"Urgent": "true"
},
"format": "TASK_UNDONE_URGENT",
"uid": "4296139695a411e79cb17054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"4295faaa95a411e79cb17054d2175f18"
]
}
} TreeLine/samples/110en_sample_basic_longtext.trln 0000644 0001750 0001750 00000007322 13262465526 021101 0 ustar doug doug {
"formats": [
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Text",
"fieldtype": "HtmlText",
"lines": 12
}
],
"formathtml": true,
"formatname": "HTML_TEXT",
"outputlines": [
"{*Name*}",
"{*Text*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Text",
"fieldtype": "Text",
"lines": 12
}
],
"formathtml": true,
"formatname": "REGULAR_TEXT",
"outputlines": [
"{*Name*}",
"{*Text*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formatname": "ROOT",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Text",
"fieldtype": "SpacedText",
"lines": 12
}
],
"formathtml": true,
"formatname": "SPACED_TEXT",
"outputlines": [
"{*Name*}{*Text*}"
],
"titleline": "{*Name*}"
}
],
"nodes": [
{
"children": [
"ed02751e95a311e79cb17054d2175f18",
"ed02771c95a311e79cb17054d2175f18",
"ed027aaa95a311e79cb17054d2175f18",
"ed027f0095a311e79cb17054d2175f18"
],
"data": {
"Name": "Text Fields"
},
"format": "ROOT",
"uid": "ed0270d295a311e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Similar to Treepad",
"Text": "This file provides a single long text field for each node. This is similar to how the Treepad program on windows is usually used."
},
"format": "REGULAR_TEXT",
"uid": "ed02751e95a311e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Regular text field",
"Text": "The most commonly used field type is regular text. Formatting such as bold, italics, font sizes and font colors can be used from the Edit menu.
\n
\nIt preserves carriage return spaces, but not multiple spaces within a line."
},
"format": "REGULAR_TEXT",
"uid": "ed02771c95a311e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "HTML text field",
"Text": "An HTML field allows tags such as italics to be added manually.\n\nIt does not preserve white space.\n\nCharacters like <, >, and & must be escaped."
},
"format": "HTML_TEXT",
"uid": "ed027aaa95a311e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Name": "Spaced text field",
"Text": "A spaced text field preserves all white space.\n\nIt does not allow character formatting."
},
"format": "SPACED_TEXT",
"uid": "ed027f0095a311e79cb17054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"ed0270d295a311e79cb17054d2175f18"
]
}
} TreeLine/samples/330en_sample_math_fields.trln 0000644 0001750 0001750 00000015640 13262465526 020361 0 ustar doug doug {
"formats": [
{
"childtype": "PART",
"fields": [
{
"fieldname": "PartNumber",
"fieldtype": "Text"
},
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"eqn": "{**Level*} + 1",
"fieldname": "Level",
"fieldtype": "Math",
"format": "#"
},
{
"fieldname": "LaborCost",
"fieldtype": "Number",
"format": "0.00",
"prefix": "$"
},
{
"eqn": "sum({*&Cost*}) + {*LaborCost*}",
"fieldname": "Cost",
"fieldtype": "Math",
"format": "0.00",
"prefix": "$"
},
{
"eqn": "{**TotalCost*}",
"fieldname": "TotalCost",
"fieldtype": "Math",
"format": "0.00",
"prefix": "$"
},
{
"eqn": "{*Cost*} / {*TotalCost*} * 100",
"fieldname": "PercentCost",
"fieldtype": "Math",
"format": "0",
"suffix": "%"
}
],
"formatname": "ASSEMBLY",
"outputlines": [
"Part {*PartNumber*}",
"{*Name*}",
"Assembly Level: {*Level*}",
"Cost: {*Cost*}",
"Percent Cost: {*PercentCost*}"
],
"titleline": "{*PartNumber*} {*Name*}"
},
{
"fields": [
{
"fieldname": "PartNumber",
"fieldtype": "Text"
},
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"eqn": "{**Level*} + 1",
"fieldname": "Level",
"fieldtype": "Math",
"format": "#"
},
{
"fieldname": "Cost",
"fieldtype": "Number",
"format": "0.00",
"prefix": "$"
},
{
"eqn": "{*Cost*} / {**TotalCost*} * 100",
"fieldname": "PercentCost",
"fieldtype": "Math",
"format": "0",
"suffix": "%"
},
{
"eqn": "('Low') if ({*PercentCost*} < 20) else ('High')",
"fieldname": "CostLevel",
"fieldtype": "Math",
"format": "#.##",
"resulttype": "text"
}
],
"formatname": "PART",
"outputlines": [
"Part {*PartNumber*}",
"{*Name*}",
"Assembly Level: {*Level*}",
"Cost: {*Cost*}",
"Percent Cost: {*PercentCost*}",
"Cost Level: {*CostLevel*}"
],
"titleline": "{*PartNumber*} {*Name*}"
},
{
"childtype": "ASSEMBLY",
"fields": [
{
"fieldname": "PartNumber",
"fieldtype": "Text"
},
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Level",
"fieldtype": "Number",
"format": "#"
},
{
"fieldname": "LaborCost",
"fieldtype": "Number",
"format": "0.00",
"prefix": "$"
},
{
"eqn": "sum({*&Cost*}) + {*LaborCost*}",
"fieldname": "TotalCost",
"fieldtype": "Math",
"format": "0.00",
"prefix": "$"
}
],
"formatname": "TOP_ASSEMBLY",
"outputlines": [
"Part {*PartNumber*}",
"{*Name*}",
"Assembly Level: {*Level*}",
"Cost: {*TotalCost*}"
],
"titleline": "{*PartNumber*} {*Name*}"
}
],
"nodes": [
{
"children": [
"5f8ff84a95a411e79cb17054d2175f18",
"5f901e5695a411e79cb17054d2175f18"
],
"data": {
"LaborCost": "3.5",
"Level": "1",
"Name": "Widget Top Assembly",
"PartNumber": "123456",
"TotalCost": "23.89"
},
"format": "TOP_ASSEMBLY",
"uid": "5f8fdd3895a411e79cb17054d2175f18"
},
{
"children": [
"5f900eb695a411e79cb17054d2175f18",
"5f901d0c95a411e79cb17054d2175f18"
],
"data": {
"Cost": "10.040000000000001",
"LaborCost": "1.75",
"Level": "2",
"Name": "Lever Assembly",
"PartNumber": "456789",
"PercentCost": "42.02595228128925",
"TotalCost": "23.89"
},
"format": "ASSEMBLY",
"uid": "5f8ff84a95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Cost": "5.4",
"CostLevel": "High",
"Level": "3",
"Name": "Lever",
"PartNumber": "987654",
"PercentCost": "22.60359983256593"
},
"format": "PART",
"uid": "5f900eb695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Cost": "2.89",
"CostLevel": "Low",
"Level": "3",
"Name": "Lever Bolt",
"PartNumber": "998877",
"PercentCost": "12.097111762243616"
},
"format": "PART",
"uid": "5f901d0c95a411e79cb17054d2175f18"
},
{
"children": [
"5f901f6e95a411e79cb17054d2175f18",
"5f90208695a411e79cb17054d2175f18"
],
"data": {
"Cost": "10.35",
"LaborCost": "2.25",
"Level": "2",
"Name": "Bracket Assembly",
"PartNumber": "112233",
"PercentCost": "43.32356634575136",
"TotalCost": "23.89"
},
"format": "ASSEMBLY",
"uid": "5f901e5695a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Cost": "6.2",
"CostLevel": "High",
"Level": "3",
"Name": "Bracket",
"PartNumber": "445566",
"PercentCost": "25.952281289242364"
},
"format": "PART",
"uid": "5f901f6e95a411e79cb17054d2175f18"
},
{
"children": [],
"data": {
"Cost": "1.9",
"CostLevel": "Low",
"Level": "3",
"Name": "Bracket Pin",
"PartNumber": "665544",
"PercentCost": "7.95311845960653"
},
"format": "PART",
"uid": "5f90208695a411e79cb17054d2175f18"
}
],
"properties": {
"tlversion": "2.9.0",
"topnodes": [
"5f8fdd3895a411e79cb17054d2175f18"
]
}
} TreeLine/samples/220en_sample_bookmarks.trln 0000644 0001750 0001750 00000030447 13262465526 020072 0 ustar doug doug {
"formats": [
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
},
{
"fieldname": "Link",
"fieldtype": "ExternalLink"
}
],
"formatname": "BOOKMARK",
"icon": "bookmark",
"outputlines": [
"{*Name*}",
"{*Link*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formatname": "FOLDER",
"icon": "folder_3",
"outputlines": [
"{*Name*}"
],
"titleline": "{*Name*}"
},
{
"fields": [
{
"fieldname": "Name",
"fieldtype": "Text"
}
],
"formathtml": true,
"formatname": "SEPARATOR",
"outputlines": [
"
{1} | '. format(self.siblingPrefix, head)) self.siblingPrefix += '
---|
tags to preserve spacing. Does not use the rich text editor. Provides methods to return formatted data. """ typeName = 'SpacedText' showRichTextInCell = False editorClassName = 'PlainTextEditor' def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the dict that defines this field's format """ super().__init__(name, formatData) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ if storedText: storedText = '{0}'.format(storedText) return super().formatOutput(storedText, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Arguments: storedText -- the source text to format """ return saxutils.unescape(storedText) def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Arguments: editorText -- the new text entered into the editor """ return saxutils.escape(editorText) def storedTextFromTitle(self, titleText): """Return new text to be stored based on title text edits. Arguments: titleText -- the new title text """ return self.storedText(titleText) class NumberField(HtmlTextField): """Class to handle a general number field format type. Stores options and format strings for a number field type. Provides methods to return formatted data. """ typeName = 'Number' defaultFormat = '#.##' evalHtmlDefault = False editorClassName = 'LineEditor' sortTypeStr = '20_num' formatHelpMenuList = [(_('Optional Digit\t#'), '#'), (_('Required Digit\t0'), '0'), (_('Digit or Space (external)\t'), ' '), ('', ''), (_('Decimal Point\t.'), '.'), (_('Decimal Comma\t,'), ','), ('', ''), (_('Comma Separator\t\\,'), '\\,'), (_('Dot Separator\t\\.'), '\\.'), (_('Space Separator (internal)\t '), ' '), ('', ''), (_('Optional Sign\t-'), '-'), (_('Required Sign\t+'), '+'), ('', ''), (_('Exponent (capital)\tE'), 'E'), (_('Exponent (small)\te'), 'e')] def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the dict that defines this field's format """ super().__init__(name, formatData) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ try: text = gennumber.GenNumber(storedText).numStr(self.format) except ValueError: text = _errorStr return super().formatOutput(text, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ if not storedText: return '' return gennumber.GenNumber(storedText).numStr(self.format) def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new text entered into the editor """ if not editorText: return '' return repr(gennumber.GenNumber().setFromStr(editorText, self.format)) def mathValue(self, node, zeroBlanks=True, noMarkup=True): """Return a numeric value to be used in math field equations. Return None if blank and not zeroBlanks, raise a ValueError if it isn't a number. Arguments: node -- the tree item storing the data zeroBlanks -- replace blank field values with zeros if True noMarkup -- not applicable to numbers """ storedText = node.data.get(self.name, '') if storedText: return gennumber.GenNumber(storedText).num return 0 if zeroBlanks else None def adjustedCompareValue(self, value): """Return value adjusted like the compareValue for use in conditionals. Number version converts to a numeric value. Arguments: value -- the comparison value to adjust """ try: return gennumber.GenNumber(value).num except ValueError: return 0 class MathField(HtmlTextField): """Class to handle a math calculation field type. Stores options and format strings for a math field type. Provides methods to return formatted data. """ typeName = 'Math' defaultFormat = '#.##' evalHtmlDefault = False fixEvalHtmlSetting = False editorClassName = 'ReadOnlyEditor' def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the attributes that define this field's format """ super().__init__(name, formatData) self.equation = None self.resultType = MathResult[formatData.get('resulttype', 'number')] equationText = formatData.get('eqn', '').strip() if equationText: self.equation = matheval.MathEquation(equationText) try: self.equation.validate() except ValueError: self.equation = None def formatData(self): """Return a dictionary of this field's attributes. Add the math equation to the standard XML output. """ formatData = super().formatData() if self.equation: formatData['eqn'] = self.equation.equationText() if self.resultType != MathResult.number: formatData['resulttype'] = self.resultType.name return formatData def setFormat(self, format): """Set the format string and initialize as required. Arguments: format -- the new format string """ if not hasattr(self, 'equation'): self.equation = None self.resultType = MathResult.number super().setFormat(format) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ text = storedText try: if self.resultType == MathResult.number: text = gennumber.GenNumber(text).numStr(self.format) elif self.resultType == MathResult.date: date = datetime.datetime.strptime(text, DateField.isoFormat).date() text = date.strftime(adjOutDateFormat(self.format)) elif self.resultType == MathResult.time: time = datetime.datetime.strptime(text, TimeField.isoFormat).time() text = time.strftime(adjOutDateFormat(self.format)) elif self.resultType == MathResult.boolean: text = genboolean.GenBoolean(text).boolStr(self.format) except ValueError: text = _errorStr return super().formatOutput(text, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ if not storedText: return '' if self.resultType == MathResult.number: return gennumber.GenNumber(storedText).numStr(self.format) if self.resultType == MathResult.date: date = datetime.datetime.strptime(storedText, DateField.isoFormat).date() editorFormat = adjOutDateFormat(globalref. genOptions['EditDateFormat']) return date.strftime(editorFormat) if self.resultType == MathResult.time: time = datetime.datetime.strptime(storedText, TimeField.isoFormat).time() editorFormat = adjOutDateFormat(globalref. genOptions['EditTimeFormat']) return time.strftime(editorFormat) if self.resultType == MathResult.boolean: return genboolean.GenBoolean(storedText).boolStr(self.format) if storedText == _errorStr: raise ValueError return storedText def equationText(self): """Return the current equation text. """ if self.equation: return self.equation.equationText() return '' def equationValue(self, node): """Return a text value from the result of the equation. Returns the '#####' error string for illegal math operations. Arguments: node -- the tree item with this equation """ if self.equation: zeroValue = _mathResultBlank[self.resultType] try: num = self.equation.equationValue(node, self.resultType, zeroValue, not self.evalHtml) except ValueError: return _errorStr if num == None: return '' if self.resultType == MathResult.date: date = DateField.refDate + datetime.timedelta(days=num) return date.strftime(DateField.isoFormat) if self.resultType == MathResult.time: dateTime = datetime.datetime.combine(DateField.refDate, TimeField.refTime) dateTime = dateTime + datetime.timedelta(seconds=num) time = dateTime.time() return time.strftime(TimeField.isoFormat) text = str(num) if not self.evalHtml: text = saxutils.escape(text) return text return '' def resultClass(self): """Return the result type's field class. """ return globals()[self.resultType.name.capitalize() + 'Field'] def changeResultType(self, resultType): """Change the result type and reset the output format. Arguments: resultType -- the new result type """ if resultType != self.resultType: self.resultType = resultType self.setFormat(self.resultClass().defaultFormat) def mathValue(self, node, zeroBlanks=True, noMarkup=True): """Return a numeric value to be used in math field equations. Return None if blank and not zeroBlanks, raise a ValueError if it isn't valid. Arguments: node -- the tree item storing the data zeroBlanks -- replace blank field values with zeros if True noMarkup -- if true, remove html markup """ storedText = node.data.get(self.name, '') if storedText: if self.resultType == MathResult.number: return gennumber.GenNumber(storedText).num if self.resultType == MathResult.date: date = datetime.datetime.strptime(storedText, DateField.isoFormat).date() return (date - DateField.refDate).days if self.resultType == MathResult.time: time = datetime.datetime.strptime(storedText, TimeField.isoFormat).time() return (time - TimeField.refTime).seconds if self.resultType == MathResult.boolean: return genboolean.GenBoolean(storedText).value if noMarkup: storedText = removeMarkup(storedText) return storedText return _mathResultBlank[self.resultType] if zeroBlanks else None def adjustedCompareValue(self, value): """Return value adjusted like the compareValue for use in conditionals. Number version converts to a numeric value. Arguments: value -- the comparison value to adjust """ try: if self.resultType == MathResult.number: return gennumber.GenNumber(value).num if self.resultType == MathResult.date: date = datetime.datetime.strptime(value, DateField.isoFormat).date() return date.strftime(DateField.isoFormat) if self.resultType == MathResult.time: time = datetime.datetime.strptime(value, TimeField.isoFormat).time() return time.strftime(TimeField.isoFormat) if self.resultType == MathResult.boolean: return genboolean.GenBoolean(value).value return value.lower() except ValueError: return 0 def sortKey(self, node): """Return a tuple with field type and comparison value for sorting. Allows different types to be sorted. Arguments: node -- the tree item storing the data """ return (self.resultClass().sortTypeStr, self.compareValue(node)) def getFormatHelpMenuList(self): """Return the list of descriptions and keys for the format help menu. """ return self.resultClass().formatHelpMenuList class NumberingField(HtmlTextField): """Class to handle formats for hierarchical node numbering. Stores options and format strings for a node numbering field type. Provides methods to return formatted node numbers. """ typeName = 'Numbering' defaultFormat = '1..' evalHtmlDefault = False editorClassName = 'LineEditor' sortTypeStr = '10_numbering' formatHelpMenuList = [(_('Number\t1'), '1'), (_('Capital Letter\tA'), 'A'), (_('Small Letter\ta'), 'a'), (_('Capital Roman Numeral\tI'), 'I'), (_('Small Roman Numeral\ti'), 'i'), ('', ''), (_('Level Separator\t/'), '/'), (_('Section Separator\t.'), '.'), ('', ''), (_('"/" Character\t//'), '//'), (_('"." Character\t..'), '..'), ('', ''), (_('Outline Example\tI../A../1../a)/i)'), 'I../A../1../a)/i)'), (_('Section Example\t1.1.1.1'), '1.1.1.1')] def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the attributes that define this field's format """ self.numFormat = None super().__init__(name, formatData) def setFormat(self, format): """Set the format string and initialize as required. Arguments: format -- the new format string """ self.numFormat = numbering.NumberingGroup(format) super().setFormat(format) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ try: text = self.numFormat.numString(storedText) except ValueError: text = _errorStr return super().formatOutput(text, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ if storedText: checkData = [int(num) for num in storedText.split('.')] return storedText def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new text entered into the editor """ if editorText: checkData = [int(num) for num in editorText.split('.')] return editorText def adjustedCompareValue(self, value): """Return value adjusted like the compareValue for use in conditionals. Number version converts to a numeric value. Arguments: value -- the comparison value to adjust """ if value: try: return [int(num) for num in value.split('.')] except ValueError: pass return [0] class DateField(HtmlTextField): """Class to handle a general date field format type. Stores options and format strings for a date field type. Provides methods to return formatted data. """ typeName = 'Date' defaultFormat = '%B %-d, %Y' isoFormat = '%Y-%m-%d' evalHtmlDefault = False fixEvalHtmlSetting = False editorClassName = 'DateEditor' refDate = datetime.date(1970, 1, 1) sortTypeStr = '40_date' formatHelpMenuList = [(_('Day (1 or 2 digits)\t%-d'), '%-d'), (_('Day (2 digits)\t%d'), '%d'), ('', ''), (_('Weekday Abbreviation\t%a'), '%a'), (_('Weekday Name\t%A'), '%A'), ('', ''), (_('Month (1 or 2 digits)\t%-m'), '%-m'), (_('Month (2 digits)\t%m'), '%m'), (_('Month Abbreviation\t%b'), '%b'), (_('Month Name\t%B'), '%B'), ('', ''), (_('Year (2 digits)\t%y'), '%y'), (_('Year (4 digits)\t%Y'), '%Y'), ('', ''), (_('Week Number (0 to 53)\t%-U'), '%-U'), (_('Day of year (1 to 366)\t%-j'), '%-j')] def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the dict that defines this field's format """ super().__init__(name, formatData) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ try: date = datetime.datetime.strptime(storedText, DateField.isoFormat).date() text = date.strftime(adjOutDateFormat(self.format)) except ValueError: text = _errorStr if not self.evalHtml: text = saxutils.escape(text) return super().formatOutput(text, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ if not storedText: return '' date = datetime.datetime.strptime(storedText, DateField.isoFormat).date() editorFormat = adjOutDateFormat(globalref.genOptions['EditDateFormat']) return date.strftime(editorFormat) def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Two digit years are interpretted as 1950-2049. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new text entered into the editor """ editorText = _multipleSpaceRegEx.sub(' ', editorText.strip()) if not editorText: return '' editorFormat = adjInDateFormat(globalref.genOptions['EditDateFormat']) try: date = datetime.datetime.strptime(editorText, editorFormat).date() except ValueError: # allow use of a 4-digit year to fix invalid dates fullYearFormat = editorFormat.replace('%y', '%Y') if fullYearFormat != editorFormat: date = datetime.datetime.strptime(editorText, fullYearFormat).date() else: raise return date.strftime(DateField.isoFormat) def getInitDefault(self): """Return the initial stored value for newly created nodes. """ if self.initDefault == _dateStampString: date = datetime.date.today() return date.strftime(DateField.isoFormat) return super().getInitDefault() def setInitDefault(self, editorText): """Set the default initial value from editor text. The function for default text field just returns the stored text. Arguments: editorText -- the new text entered into the editor """ if editorText == _dateStampString: self.initDefault = _dateStampString else: super().setInitDefault(editorText) def getEditorInitDefault(self): """Return initial value in editor format. """ if self.initDefault == _dateStampString: return _dateStampString return super().getEditorInitDefault() def initDefaultChoices(self): """Return a list of choices for setting the init default. """ return [_dateStampString] def mathValue(self, node, zeroBlanks=True, noMarkup=True): """Return a numeric value to be used in math field equations. Return None if blank and not zeroBlanks, raise a ValueError if it isn't a valid date. Arguments: node -- the tree item storing the data zeroBlanks -- replace blank field values with zeros if True """ storedText = node.data.get(self.name, '') if storedText: date = datetime.datetime.strptime(storedText, DateField.isoFormat).date() return (date - DateField.refDate).days return 0 if zeroBlanks else None def compareValue(self, node): """Return a value for comparison to other nodes and for sorting. Returns lowercase text for text fields or numbers for non-text fields. Date field uses ISO date format (YYY-MM-DD). Arguments: node -- the tree item storing the data """ return node.data.get(self.name, '') def adjustedCompareValue(self, value): """Return value adjusted like the compareValue for use in conditionals. Date version converts to an ISO date format (YYYY-MM-DD). Arguments: value -- the comparison value to adjust """ value = _multipleSpaceRegEx.sub(' ', value.strip()) if not value: return '' if value == _dateStampString: date = datetime.date.today() return date.strftime(DateField.isoFormat) try: return self.storedText(value) except ValueError: return value class TimeField(HtmlTextField): """Class to handle a general time field format type Stores options and format strings for a time field type. Provides methods to return formatted data. """ typeName = 'Time' defaultFormat = '%-I:%M:%S %p' isoFormat = '%H:%M:%S.%f' evalHtmlDefault = False fixEvalHtmlSetting = False editorClassName = 'TimeEditor' numChoiceColumns = 2 autoAddChoices = False refTime = datetime.time() sortTypeStr = '50_time' formatHelpMenuList = [(_('Hour (0-23, 1 or 2 digits)\t%-H'), '%-H'), (_('Hour (00-23, 2 digits)\t%H'), '%H'), (_('Hour (1-12, 1 or 2 digits)\t%-I'), '%-I'), (_('Hour (01-12, 2 digits)\t%I'), '%I'), ('', ''), (_('Minute (1 or 2 digits)\t%-M'), '%-M'), (_('Minute (2 digits)\t%M'), '%M'), ('', ''), (_('Second (1 or 2 digits)\t%-S'), '%-S'), (_('Second (2 digits)\t%S'), '%S'), ('', ''), (_('Microseconds (6 digits)\t%f'), '%f'), ('', ''), (_('AM/PM\t%p'), '%p')] def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the attributes that define this field's format """ super().__init__(name, formatData) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ try: time = datetime.datetime.strptime(storedText, TimeField.isoFormat).time() outFormat = adjOutDateFormat(self.format) outFormat = adjTimeAmPm(outFormat, time) text = time.strftime(outFormat) except ValueError: text = _errorStr if not self.evalHtml: text = saxutils.escape(text) return super().formatOutput(text, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ if not storedText: return '' time = datetime.datetime.strptime(storedText, TimeField.isoFormat).time() editorFormat = adjOutDateFormat(globalref.genOptions['EditTimeFormat']) editorFormat = adjTimeAmPm(editorFormat, time) return time.strftime(editorFormat) def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new text entered into the editor """ editorText = _multipleSpaceRegEx.sub(' ', editorText.strip()) if not editorText: return '' editorFormat = adjInDateFormat(globalref.genOptions['EditTimeFormat']) time = None try: time = datetime.datetime.strptime(editorText, editorFormat).time() except ValueError: noSecFormat = editorFormat.replace(':%S', '') noSecFormat = _multipleSpaceRegEx.sub(' ', noSecFormat.strip()) try: time = datetime.datetime.strptime(editorText, noSecFormat).time() except ValueError: for altFormat in (editorFormat, noSecFormat): noAmFormat = altFormat.replace('%p', '') noAmFormat = _multipleSpaceRegEx.sub(' ', noAmFormat.strip()) try: time = datetime.datetime.strptime(editorText, noAmFormat).time() break except ValueError: pass if not time: raise ValueError return time.strftime(TimeField.isoFormat) def annotatedComboChoices(self, editorText): """Return a list of (choice, annotation) tuples for the combo box. Arguments: editorText -- the text entered into the editor """ editorFormat = adjOutDateFormat(globalref.genOptions['EditTimeFormat']) choices = [(datetime.datetime.now().time().strftime(editorFormat), '({0})'.format(_timeStampString))] for hour in (6, 9, 12, 15, 18, 21, 0): choices.append((datetime.time(hour).strftime(editorFormat), '')) return choices def getInitDefault(self): """Return the initial stored value for newly created nodes. """ if self.initDefault == _timeStampString: time = datetime.datetime.now().time() return time.strftime(TimeField.isoFormat) return super().getInitDefault() def setInitDefault(self, editorText): """Set the default initial value from editor text. The function for default text field just returns the stored text. Arguments: editorText -- the new text entered into the editor """ if editorText == _timeStampString: self.initDefault = _timeStampString else: super().setInitDefault(editorText) def getEditorInitDefault(self): """Return initial value in editor format. """ if self.initDefault == _timeStampString: return _timeStampString return super().getEditorInitDefault() def initDefaultChoices(self): """Return a list of choices for setting the init default. """ return [_timeStampString] def mathValue(self, node, zeroBlanks=True, noMarkup=True): """Return a numeric value to be used in math field equations. Return None if blank and not zeroBlanks, raise a ValueError if it isn't a valid time. Arguments: node -- the tree item storing the data zeroBlanks -- replace blank field values with zeros if True """ storedText = node.data.get(self.name, '') if storedText: time = datetime.datetime.strptime(storedText, TimeField.isoFormat).time() dateTime = datetime.datetime.combine(DateField.refDate, time) refDateTime = datetime.datetime.combine(DateField.refDate, TimeField.refTime) return (dateTime - refDateTime).seconds return 0 if zeroBlanks else None def compareValue(self, node): """Return a value for comparison to other nodes and for sorting. Returns lowercase text for text fields or numbers for non-text fields. Time field uses HH:MM:SS format. Arguments: node -- the tree item storing the data """ return node.data.get(self.name, '') def adjustedCompareValue(self, value): """Return value adjusted like the compareValue for use in conditionals. Time version converts to HH:MM:SS format. Arguments: value -- the comparison value to adjust """ value = _multipleSpaceRegEx.sub(' ', value.strip()) if not value: return '' if value == _timeStampString: time = datetime.datetime.now().time() return time.strftime(TimeField.isoFormat) try: return self.storedText(value) except ValueError: return value class DateTimeField(HtmlTextField): """Class to handle a general date and time field format type. Stores options and format strings for a date and time field type. Provides methods to return formatted data. """ typeName = 'DateTime' defaultFormat = '%B %-d, %Y %-I:%M:%S %p' isoFormat = '%Y-%m-%d %H:%M:%S.%f' evalHtmlDefault = False fixEvalHtmlSetting = False editorClassName = 'DateTimeEditor' refDateTime = datetime.datetime(1970, 1, 1) sortTypeStr ='45_datetime' formatHelpMenuList = [(_('Day (1 or 2 digits)\t%-d'), '%-d'), (_('Day (2 digits)\t%d'), '%d'), ('', ''), (_('Weekday Abbreviation\t%a'), '%a'), (_('Weekday Name\t%A'), '%A'), ('', ''), (_('Month (1 or 2 digits)\t%-m'), '%-m'), (_('Month (2 digits)\t%m'), '%m'), (_('Month Abbreviation\t%b'), '%b'), (_('Month Name\t%B'), '%B'), ('', ''), (_('Year (2 digits)\t%y'), '%y'), (_('Year (4 digits)\t%Y'), '%Y'), ('', ''), (_('Week Number (0 to 53)\t%-U'), '%-U'), (_('Day of year (1 to 366)\t%-j'), '%-j'), (_('Hour (0-23, 1 or 2 digits)\t%-H'), '%-H'), (_('Hour (00-23, 2 digits)\t%H'), '%H'), (_('Hour (1-12, 1 or 2 digits)\t%-I'), '%-I'), (_('Hour (01-12, 2 digits)\t%I'), '%I'), ('', ''), (_('Minute (1 or 2 digits)\t%-M'), '%-M'), (_('Minute (2 digits)\t%M'), '%M'), ('', ''), (_('Second (1 or 2 digits)\t%-S'), '%-S'), (_('Second (2 digits)\t%S'), '%S'), ('', ''), (_('Microseconds (6 digits)\t%f'), '%f'), ('', ''), (_('AM/PM\t%p'), '%p')] def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the dict that defines this field's format """ super().__init__(name, formatData) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ try: dateTime = datetime.datetime.strptime(storedText, DateTimeField.isoFormat) outFormat = adjOutDateFormat(self.format) outFormat = adjTimeAmPm(outFormat, dateTime) text = dateTime.strftime(outFormat) except ValueError: text = _errorStr if not self.evalHtml: text = saxutils.escape(text) return super().formatOutput(text, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ if not storedText: return '' dateTime = datetime.datetime.strptime(storedText, DateTimeField.isoFormat) editorFormat = '{0} {1}'.format(globalref.genOptions['EditDateFormat'], globalref.genOptions['EditTimeFormat']) editorFormat = adjOutDateFormat(editorFormat) editorFormat = adjTimeAmPm(editorFormat, dateTime) return dateTime.strftime(editorFormat) def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Two digit years are interpretted as 1950-2049. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new text entered into the editor """ editorText = _multipleSpaceRegEx.sub(' ', editorText.strip()) if not editorText: return '' editorFormat = '{0} {1}'.format(globalref.genOptions['EditDateFormat'], globalref.genOptions['EditTimeFormat']) editorFormat = adjInDateFormat(editorFormat) dateTime = None try: dateTime = datetime.datetime.strptime(editorText, editorFormat) except ValueError: noSecFormat = editorFormat.replace(':%S', '') noSecFormat = _multipleSpaceRegEx.sub(' ', noSecFormat.strip()) altFormats = [editorFormat, noSecFormat] for altFormat in altFormats[:]: noAmFormat = altFormat.replace('%p', '') noAmFormat = _multipleSpaceRegEx.sub(' ', noAmFormat.strip()) altFormats.append(noAmFormat) for altFormat in altFormats[:]: fullYearFormat = altFormat.replace('%y', '%Y') altFormats.append(fullYearFormat) for editorFormat in altFormats[1:]: try: dateTime = datetime.datetime.strptime(editorText, editorFormat) break except ValueError: pass if not dateTime: raise ValueError return dateTime.strftime(DateTimeField.isoFormat) def getInitDefault(self): """Return the initial stored value for newly created nodes. """ if self.initDefault == _timeStampString: dateTime = datetime.datetime.now() return dateTime.strftime(DateTimeField.isoFormat) return super().getInitDefault() def setInitDefault(self, editorText): """Set the default initial value from editor text. The function for default text field just returns the stored text. Arguments: editorText -- the new text entered into the editor """ if editorText == _timeStampString: self.initDefault = _timeStampString else: super().setInitDefault(editorText) def getEditorInitDefault(self): """Return initial value in editor format. """ if self.initDefault == _timeStampString: return _timeStampString return super().getEditorInitDefault() def initDefaultChoices(self): """Return a list of choices for setting the init default. """ return [_timeStampString] def mathValue(self, node, zeroBlanks=True, noMarkup=True): """Return a numeric value to be used in math field equations. Return None if blank and not zeroBlanks, raise a ValueError if it isn't a valid time. Arguments: node -- the tree item storing the data zeroBlanks -- replace blank field values with zeros if True """ storedText = node.data.get(self.name, '') if storedText: dateTime = datetime.datetime.strptime(storedText, DateTimeField.isoFormat) return (dateTime - DateTimeField.refDateTime).total_seconds() return 0 if zeroBlanks else None def compareValue(self, node): """Return a value for comparison to other nodes and for sorting. Returns lowercase text for text fields or numbers for non-text fields. DateTime field uses YYYY-MM-DD HH:MM:SS format. Arguments: node -- the tree item storing the data """ return node.data.get(self.name, '') def adjustedCompareValue(self, value): """Return value adjusted like the compareValue for use in conditionals. Time version converts to HH:MM:SS format. Arguments: value -- the comparison value to adjust """ value = _multipleSpaceRegEx.sub(' ', value.strip()) if not value: return '' if value == _timeStampString: dateTime = datetime.datetime.now() return dateTime.strftime(DateTimeField.isoFormat) try: return self.storedText(value) except ValueError: return value class ChoiceField(HtmlTextField): """Class to handle a field with pre-defined, individual text choices. Stores options and format strings for a choice field type. Provides methods to return formatted data. """ typeName = 'Choice' editSep = '/' defaultFormat = '1/2/3/4' evalHtmlDefault = False fixEvalHtmlSetting = False editorClassName = 'ComboEditor' numChoiceColumns = 1 autoAddChoices = False formatHelpMenuList = [(_('Separator\t/'), '/'), ('', ''), (_('"/" Character\t//'), '//'), ('', ''), (_('Example\t1/2/3/4'), '1/2/3/4')] def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the dict that defines this field's format """ super().__init__(name, formatData) def setFormat(self, format): """Set the format string and initialize as required. Arguments: format -- the new format string """ super().setFormat(format) self.choiceList = self.splitText(self.format) if self.evalHtml: self.choices = set(self.choiceList) else: self.choices = set([saxutils.escape(choice) for choice in self.choiceList]) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ if storedText not in self.choices: storedText = _errorStr return super().formatOutput(storedText, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ if storedText and storedText not in self.choices: raise ValueError if self.evalHtml: return storedText return saxutils.unescape(storedText) def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new text entered into the editor """ if not self.evalHtml: editorText = saxutils.escape(editorText) if not editorText or editorText in self.choices: return editorText raise ValueError def comboChoices(self): """Return a list of choices for the combo box. """ return self.choiceList def initDefaultChoices(self): """Return a list of choices for setting the init default. """ return self.choiceList def splitText(self, textStr): """Split textStr using editSep, return a list of strings. Double editSep's are not split (become single). Removes duplicates and empty strings. Arguments: textStr -- the text to split """ result = [] textStr = textStr.replace(self.editSep * 2, '\0') for text in textStr.split(self.editSep): text = text.strip().replace('\0', self.editSep) if text and text not in result: result.append(text) return result class AutoChoiceField(HtmlTextField): """Class to handle a field with automatically populated text choices. Stores options and possible entries for an auto-choice field type. Provides methods to return formatted data. """ typeName = 'AutoChoice' evalHtmlDefault = False fixEvalHtmlSetting = False editorClassName = 'ComboEditor' numChoiceColumns = 1 autoAddChoices = True def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the attributes that define this field's format """ super().__init__(name, formatData) self.choices = set() def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Arguments: storedText -- the source text to format """ if self.evalHtml: return storedText return saxutils.unescape(storedText) def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Arguments: editorText -- the new text entered into the editor """ if self.evalHtml: return editorText return saxutils.escape(editorText) def comboChoices(self): """Return a list of choices for the combo box. """ if self.evalHtml: choices = self.choices else: choices = [saxutils.unescape(text) for text in self.choices] return sorted(choices, key=str.lower) def addChoice(self, text): """Add a new choice. Arguments: text -- the choice to be added """ if text: self.choices.add(text) def clearChoices(self): """Remove all current choices. """ self.choices = set() class CombinationField(ChoiceField): """Class to handle a field with multiple pre-defined text choices. Stores options and format strings for a combination field type. Provides methods to return formatted data. """ typeName = 'Combination' editorClassName = 'CombinationEditor' numChoiceColumns = 2 def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the dict that defines this field's format """ super().__init__(name, formatData) def setFormat(self, format): """Set the format string and initialize as required. Arguments: format -- the new format string """ TextField.setFormat(self, format) if not self.evalHtml: format = saxutils.escape(format) self.choiceList = self.splitText(format) self.choices = set(self.choiceList) self.outputSep = '' def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None): """Return formatted output text for this field in this node. Sets output separator prior to calling base class methods. Arguments: node -- the tree item storing the data oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix spotRef -- optional, used for ancestor field refs """ self.outputSep = node.formatRef.outputSeparator return super().outputText(node, oneLine, noHtml, formatHtml, spotRef) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ selections, valid = self.sortedSelections(storedText) if valid: result = self.outputSep.join(selections) else: result = _errorStr return TextField.formatOutput(self, result, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ selections = set(self.splitText(storedText)) if selections.issubset(self.choices): if self.evalHtml: return storedText return saxutils.unescape(storedText) raise ValueError def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new text entered into the editor """ if not self.evalHtml: editorText = saxutils.escape(editorText) selections, valid = self.sortedSelections(editorText) if not valid: raise ValueError return self.joinText(selections) def comboChoices(self): """Return a list of choices for the combo box. """ if self.evalHtml: return self.choiceList return [saxutils.unescape(text) for text in self.choiceList] def comboActiveChoices(self, editorText): """Return a sorted list of choices currently in editorText. Arguments: editorText -- the text entered into the editor """ selections, valid = self.sortedSelections(saxutils.escape(editorText)) if self.evalHtml: return selections return [saxutils.unescape(text) for text in selections] def initDefaultChoices(self): """Return a list of choices for setting the init default. """ return [] def sortedSelections(self, inText): """Split inText using editSep and sort like format string. Return a tuple of resulting selection list and bool validity. Valid if all choices are in the format string. Arguments: inText -- the text to split and sequence """ selections = set(self.splitText(inText)) result = [text for text in self.choiceList if text in selections] return (result, len(selections) == len(result)) def joinText(self, textList): """Join the text list using editSep, return the string. Any editSep in text items become double. Arguments: textList -- the list of text items to join """ return self.editSep.join([text.replace(self.editSep, self.editSep * 2) for text in textList]) class AutoCombinationField(CombinationField): """Class for a field with multiple automatically populated text choices. Stores options and possible entries for an auto-choice field type. Provides methods to return formatted data. """ typeName = 'AutoCombination' autoAddChoices = True defaultFormat = '' formatHelpMenuList = [] def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the attributes that define this field's format """ super().__init__(name, formatData) self.choices = set() self.outputSep = '' def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None): """Return formatted output text for this field in this node. Sets output separator prior to calling base class methods. Arguments: node -- the tree item storing the data oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix spotRef -- optional, used for ancestor field refs """ self.outputSep = node.formatRef.outputSeparator return super().outputText(node, oneLine, noHtml, formatHtml, spotRef) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ result = self.outputSep.join(self.splitText(storedText)) return TextField.formatOutput(self, result, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Arguments: storedText -- the source text to format """ if self.evalHtml: return storedText return saxutils.unescape(storedText) def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Also resets outputSep, to be defined at the next output. Arguments: editorText -- the new text entered into the editor """ self.outputSep = '' if not self.evalHtml: editorText = saxutils.escape(editorText) selections = sorted(self.splitText(editorText), key=str.lower) return self.joinText(selections) def comboChoices(self): """Return a list of choices for the combo box. """ if self.evalHtml: choices = self.choices else: choices = [saxutils.unescape(text) for text in self.choices] return sorted(choices, key=str.lower) def comboActiveChoices(self, editorText): """Return a sorted list of choices currently in editorText. Arguments: editorText -- the text entered into the editor """ selections, valid = self.sortedSelections(saxutils.escape(editorText)) if self.evalHtml: return selections return [saxutils.unescape(text) for text in selections] def sortedSelections(self, inText): """Split inText using editSep and sort like format string. Return a tuple of resulting selection list and bool validity. This version always returns valid. Arguments: inText -- the text to split and sequence """ selections = sorted(self.splitText(inText), key=str.lower) return (selections, True) def addChoice(self, text): """Add a new choice. Arguments: text -- the stored text combinations to be added """ for choice in self.splitText(text): self.choices.add(choice) def clearChoices(self): """Remove all current choices. """ self.choices = set() class BooleanField(ChoiceField): """Class to handle a general boolean field format type. Stores options and format strings for a boolean field type. Provides methods to return formatted data. """ typeName = 'Boolean' defaultFormat = _('yes/no') evalHtmlDefault = False fixEvalHtmlSetting = False sortTypeStr ='30_bool' formatHelpMenuList = [(_('true/false'), 'true/false'), (_('T/F'), 'T/F'), ('', ''), (_('yes/no'), 'yes/no'), (_('Y/N'), 'Y/N'), ('', ''), ('1/0', '1/0')] def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the dict that defines this field's format """ super().__init__(name, formatData) def setFormat(self, format): """Set the format string and initialize as required. Arguments: format -- the new format string """ HtmlTextField.setFormat(self, format) self.strippedFormat = removeMarkup(self.format) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ try: text = genboolean.GenBoolean(storedText).boolStr(self.format) except ValueError: text = _errorStr if not self.evalHtml: text = saxutils.escape(text) return HtmlTextField.formatOutput(self, text, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ if not storedText: return '' boolFormat = self.strippedFormat if self.evalHtml else self.format return genboolean.GenBoolean(storedText).boolStr(boolFormat) def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new text entered into the editor """ if not editorText: return '' boolFormat = self.strippedFormat if self.evalHtml else self.format try: return repr(genboolean.GenBoolean().setFromStr(editorText, boolFormat)) except ValueError: return repr(genboolean.GenBoolean(editorText)) def comboChoices(self): """Return a list of choices for the combo box. """ if self.evalHtml: return self.splitText(self.strippedFormat) return self.splitText(self.format) def initDefaultChoices(self): """Return a list of choices for setting the init default. """ return self.comboChoices() def mathValue(self, node, zeroBlanks=True, noMarkup=True): """Return a value to be used in math field equations. Return None if blank and not zeroBlanks, raise a ValueError if it isn't a valid boolean. Arguments: node -- the tree item storing the data zeroBlanks -- replace blank field values with zeros if True """ storedText = node.data.get(self.name, '') if storedText: return genboolean.GenBoolean(storedText).value return False if zeroBlanks else None def compareValue(self, node): """Return a value for comparison to other nodes and for sorting. Returns lowercase text for text fields or numbers for non-text fields. Bool fields return True or False values. Arguments: node -- the tree item storing the data """ storedText = node.data.get(self.name, '') try: return genboolean.GenBoolean(storedText).value except ValueError: return False def adjustedCompareValue(self, value): """Return value adjusted like the compareValue for use in conditionals. Bool version converts to a bool value. Arguments: value -- the comparison value to adjust """ try: return genboolean.GenBoolean().setFromStr(value, self.format).value except ValueError: try: return genboolean.GenBoolean(value).value except ValueError: return False class ExternalLinkField(HtmlTextField): """Class to handle a field containing various types of external HTML links. Protocol choices include http, https, file, mailto. Stores data as HTML tags, shows in editors as "protocol:address [name]". """ typeName = 'ExternalLink' evalHtmlDefault = False editorClassName = 'ExtLinkEditor' sortTypeStr ='60_link' def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the attributes that define this field's format """ super().__init__(name, formatData) def addressAndName(self, storedText): """Return the link title and the name from the given stored link. Raise ValueError if the stored text is not formatted as a link. Arguments: storedText -- the source text to format """ if not storedText: return ('', '') linkMatch = linkRegExp.search(storedText) if not linkMatch: raise ValueError address, name = linkMatch.groups() return (address, name) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ if noHtml: linkMatch = linkRegExp.search(storedText) if linkMatch: address, name = linkMatch.groups() storedText = name.strip() if not storedText: storedText = address.lstrip('#') return super().formatOutput(storedText, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ if not storedText: return '' address, name = self.addressAndName(storedText) name = name.strip() if not name: name = urltools.shortName(address) return '{0} [{1}]'.format(address, name) def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new text entered into the editor """ if not editorText: return '' nameMatch = linkSeparateNameRegExp.match(editorText) if nameMatch: address, name = nameMatch.groups() else: raise ValueError return '{1}'.format(address.strip(), name.strip()) def adjustedCompareValue(self, value): """Return value adjusted like the compareValue for use in conditionals. Link fields use link address. Arguments: value -- the comparison value to adjust """ if not value: return '' try: address, name = self.addressAndName(value) except ValueError: return value.lower() return address.lstrip('#').lower() class InternalLinkField(ExternalLinkField): """Class to handle a field containing internal links to nodes. Stores data as HTML local link tag, shows in editors as "id [name]". """ typeName = 'InternalLink' editorClassName = 'IntLinkEditor' supportsInitDefault = False def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the attributes that define this field's format """ super().__init__(name, formatData) def editorText(self, node): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Also raises a ValueError if the link is not a valid destination, with the editor text as the second argument to the exception. Arguments: node -- the tree item storing the data """ storedText = node.data.get(self.name, '') return self.formatEditorText(storedText, node.treeStructureRef()) def formatEditorText(self, storedText, treeStructRef): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Also raises a ValueError if the link is not a valid destination, with the editor text as the second argument to the exception. Arguments: storedText -- the source text to format treeStructRef -- ref to the tree structure to get the linked title """ if not storedText: return '' address, name = self.addressAndName(storedText) address = address.lstrip('#') targetNode = treeStructRef.nodeDict.get(address, None) linkTitle = targetNode.title() if targetNode else _errorStr name = name.strip() if not name and targetNode: name = linkTitle result = 'LinkTo: {0} [{1}]'.format(linkTitle, name) if linkTitle == _errorStr: raise ValueError('invalid address', result) return result def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Uses the "address [name]" format as input, not the final editor form. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new editor text in "address [name]" format """ if not editorText: return '' nameMatch = linkSeparateNameRegExp.match(editorText) if not nameMatch: raise ValueError address, name = nameMatch.groups() if not address: raise ValueError('invalid address', '') if not name: name = _errorStr result = '{1}'.format(address.strip(), name.strip()) if name == _errorStr: raise ValueError('invalid name', result) return result class PictureField(HtmlTextField): """Class to handle a field containing various types of external HTML links. Protocol choices include http, https, file, mailto. Stores data as HTML tags, shows in editors as "protocol:address [name]". """ typeName = 'Picture' evalHtmlDefault = False editorClassName = 'PictureLinkEditor' sortTypeStr ='60_link' def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the attributes that define this field's format """ super().__init__(name, formatData) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ if noHtml: linkMatch = _imageRegExp.search(storedText) if linkMatch: address = linkMatch.group(1) storedText = address.strip() return super().formatOutput(storedText, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ if not storedText: return '' linkMatch = _imageRegExp.search(storedText) if not linkMatch: raise ValueError return linkMatch.group(1) def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new text entered into the editor """ editorText = editorText.strip() if not editorText: return '' nameMatch = linkSeparateNameRegExp.match(editorText) if nameMatch: address, name = nameMatch.groups() else: address = editorText name = urltools.shortName(address) return ' '.format(editorText) def adjustedCompareValue(self, value): """Return value adjusted like the compareValue for use in conditionals. Link fields use link address. Arguments: value -- the comparison value to adjust """ if not value: return '' linkMatch = _imageRegExp.search(value) if not linkMatch: return value.lower() return linkMatch.group(1).lower() class RegularExpressionField(HtmlTextField): """Class to handle a field format type controlled by a regular expression. Stores options and format strings for a number field type. Provides methods to return formatted data. """ typeName = 'RegularExpression' defaultFormat = '.*' evalHtmlDefault = False fixEvalHtmlSetting = False editorClassName = 'LineEditor' formatHelpMenuList = [(_('Any Character\t.'), '.'), (_('End of Text\t$'), '$'), ('', ''), (_('0 Or More Repetitions\t*'), '*'), (_('1 Or More Repetitions\t+'), '+'), (_('0 Or 1 Repetitions\t?'), '?'), ('', ''), (_('Set of Numbers\t[0-9]'), '[0-9]'), (_('Lower Case Letters\t[a-z]'), '[a-z]'), (_('Upper Case Letters\t[A-Z]'), '[A-Z]'), (_('Not a Number\t[^0-9]'), '[^0-9]'), ('', ''), (_('Or\t|'), '|'), (_('Escape a Special Character\t\\'), '\\')] def __init__(self, name, formatData=None): """Initialize a field format type. Arguments: name -- the field name string formatData -- the dict that defines this field's format """ super().__init__(name, formatData) def setFormat(self, format): """Set the format string and initialize as required. Raise a ValueError if the format is illegal. Arguments: format -- the new format string """ try: re.compile(format) except re.error: raise ValueError super().setFormat(format) def formatOutput(self, storedText, oneLine, noHtml, formatHtml): """Return formatted output text from stored text for this field. Arguments: storedText -- the source text to format oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix """ match = re.fullmatch(self.format, saxutils.unescape(storedText)) if not storedText or match: text = storedText else: text = _errorStr return super().formatOutput(text, oneLine, noHtml, formatHtml) def formatEditorText(self, storedText): """Return text formatted for use in the data editor. Raises a ValueError if the data does not match the format. Arguments: storedText -- the source text to format """ if not self.evalHtml: storedText = saxutils.unescape(storedText) match = re.fullmatch(self.format, storedText) if not storedText or match: return storedText raise ValueError def storedText(self, editorText): """Return new text to be stored based on text from the data editor. Raises a ValueError if the data does not match the format. Arguments: editorText -- the new text entered into the editor """ match = re.fullmatch(self.format, editorText) if not editorText or match: if self.evalHtml: return editorText return saxutils.escape(editorText) raise ValueError class AncestorLevelField(TextField): """Placeholder format for ref. to ancestor fields at specific levels. """ typeName = 'AncestorLevel' def __init__(self, name, ancestorLevel=1): """Initialize a field format placeholder type. Arguments: name -- the field name string ancestorLevel -- the number of generations to go back """ super().__init__(name, {}) self.ancestorLevel = ancestorLevel def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None): """Return formatted output text for this field in this node. Finds the appropriate ancestor node to get the field text. Arguments: node -- the tree node to start from oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix spotRef -- optional, used for ancestor field refs """ if not spotRef: spotRef = node.spotByNumber(0) for num in range(self.ancestorLevel): spotRef = spotRef.parentSpot if not spotRef: return '' try: field = spotRef.nodeRef.formatRef.fieldDict[self.name] except (AttributeError, KeyError): return '' return field.outputText(spotRef.nodeRef, oneLine, noHtml, formatHtml, spotRef) def sepName(self): """Return the name enclosed with {* *} separators """ return '{{*{0}{1}*}}'.format(self.ancestorLevel * '*', self.name) class AnyAncestorField(TextField): """Placeholder format for ref. to matching ancestor fields at any level. """ typeName = 'AnyAncestor' def __init__(self, name): """Initialize a field format placeholder type. Arguments: name -- the field name string """ super().__init__(name, {}) def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None): """Return formatted output text for this field in this node. Finds the appropriate ancestor node to get the field text. Arguments: node -- the tree node to start from oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix spotRef -- optional, used for ancestor field refs """ if not spotRef: spotRef = node.spotByNumber(0) while spotRef.parentSpot: spotRef = spotRef.parentSpot try: field = spotRef.nodeRef.formatRef.fieldDict[self.name] except (AttributeError, KeyError): pass else: return field.outputText(spotRef.nodeRef, oneLine, noHtml, formatHtml, spotRef) return '' def sepName(self): """Return the name enclosed with {* *} separators """ return '{{*?{0}*}}'.format(self.name) class ChildListField(TextField): """Placeholder format for ref. to matching ancestor fields at any level. """ typeName = 'ChildList' def __init__(self, name): """Initialize a field format placeholder type. Arguments: name -- the field name string """ super().__init__(name, {}) def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None): """Return formatted output text for this field in this node. Returns a joined list of matching child field data. Arguments: node -- the tree node to start from oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix spotRef -- optional, used for ancestor field refs """ result = [] for child in node.childList: try: field = child.formatRef.fieldDict[self.name] except KeyError: pass else: result.append(field.outputText(child, oneLine, noHtml, formatHtml, spotRef)) outputSep = node.formatRef.outputSeparator return outputSep.join(result) def sepName(self): """Return the name enclosed with {* *} separators """ return '{{*&{0}*}}'.format(self.name) class DescendantCountField(TextField): """Placeholder format for count of descendants at a given level. """ typeName = 'DescendantCount' def __init__(self, name, descendantLevel=1): """Initialize a field format placeholder type. Arguments: name -- the field name string descendantLevel -- the level to descend to """ super().__init__(name, {}) self.descendantLevel = descendantLevel def outputText(self, node, oneLine, noHtml, formatHtml, spotRef=None): """Return formatted output text for this field in this node. Returns a count of descendants at the approriate level. Arguments: node -- the tree node to start from oneLine -- if True, returns only first line of output (for titles) noHtml -- if True, removes all HTML markup (for titles, etc.) formatHtml -- if False, escapes HTML from prefix & suffix spotRef -- optional, used for ancestor field refs """ newNodes = [node] for i in range(self.descendantLevel): prevNodes = newNodes newNodes = [] for child in prevNodes: newNodes.extend(child.childList) return repr(len(newNodes)) def sepName(self): """Return the name enclosed with {* *} separators """ return '{{*#{0}*}}'.format(self.name) #### Utility Functions #### def removeMarkup(text): """Return text with all HTML Markup removed and entities unescaped. Any
tags are replaced with newlines. """ text = _lineBreakRegEx.sub('\n', text) text = _stripTagRe.sub('', text) return saxutils.unescape(text) def adjOutDateFormat(dateFormat): """Replace Linux lead zero removal with Windows version in date formats. Arguments: dateFormat -- the format to modify """ if sys.platform.startswith('win'): dateFormat = dateFormat.replace('%-', '%#') return dateFormat def adjInDateFormat(dateFormat): """Remove lead zero formatting in date formats for reading dates. Arguments: dateFormat -- the format to modify """ return dateFormat.replace('%-', '%') def adjTimeAmPm(timeFormat, time): """Add AM/PM to timeFormat if in format and locale skips it. Arguments: timeFormat -- the format to modify time -- the datetime object to check for AM/PM """ if '%p' in timeFormat and time.strftime('%I (%p)').endswith('()'): amPm = 'AM' if time.hour < 12 else 'PM' timeFormat = re.sub(r'(?