Creating Manual ParseResults in Pyparsing - pyparsing

Crossposted at http://pyparsing.wikispaces.com/message/view/home/49765026
I am working on a parsing project where I need to inject some manually created parseresults into a parsed token. I have attached a parseaction to the appropriate place in my code, and I seem to have succeeded in creating a custom made parseresult to add back into my larger grammer. dump() and asxml() seem to output correctly, but other parts of my code (trying to access the created results by name) have issues. I can access by list position, but not assigned name. It is entirely possible that my limited python knowledge is messing me up somewhere, but since I have not been able to find an example of creating a parseresults quite this way I thought I would start here. Here is my parseresults creation code. tripHeaderCustomFields is attached as a parseaction. If a particular value is parsed (ie. "TripCode") then some custom parseresults are created and added back in to the final result.
If anyone has tried to create manual parseresults like this, could you please look over my code and tell me if you see any glaring problems? It took hours of trial and error to get this version to work, and I would not be surprised if there is a better or more correct way.
def addCustomField( self, group, name, datatype, value ):
"""
custom fields:
Group: ie, specific airline or category - "USAir, "general"
Name: name of field, ie "linecheck", "Medical", "Deadhead", "IV Pay"
DataType: string, int, date, time
Value: value of field, ie. "checked by joe shmo, #2345", or "1st class medical - bryman"
"""
#TODO: Need to ask for help, some logic problem somewhere. loosing string name somewhere, but xml prints ok!
prGroup = ParseResults( group, self.NAME.CFGROUP )
prName = ParseResults( name, self.NAME.CFNAME )
prDataType = ParseResults( datatype, self.NAME.CFDATATYPE )
prValue = ParseResults( value, self.NAME.CFVAULE )
prList = ParseResults( [] )
prList += prGroup
prList += prName
prList += prDataType
prList += prValue
customField = ParseResults( [prList], self.NAME.CUSTOMFIELD )
return customField
def tripHeaderCustomFields( self, tokens ):
parseSegment = tokens
if "TripCode" in parseSegment:
customField = self.addCustomField( "USAir", "PairingCode", "String", parseSegment["TripCode"] )
if self.NAME.CUSTOMFIELDS in parseSegment:
parseSegment[self.NAME.CUSTOMFIELDS] += customField
else :
parseSegment += ParseResults( [customField], self.NAME.CUSTOMFIELDS )
if "Charter" in parseSegment[self.NAME.EFFECTIVEDOWS]:
customField = self.addCustomField( "USAir", "Charter", "Boolean", "True" )
if self.NAME.CUSTOMFIELDS in parseSegment:
parseSegment[self.NAME.CUSTOMFIELDS] += customField
else :
parseSegment += ParseResults( [customField], self.NAME.CUSTOMFIELDS )
return tokens
returns a seemingly correct token,
<CustomFields>
<CustomField>
<Group>USAir</Group>
<Name>EquipmentChange</Name>
<DataType>Boolean</DataType>
<Value>True</Value>
</CustomField>
<CustomField>
<Group>USAir</Group>
<Name>EquipmentChange</Name>
<DataType>Boolean</DataType>
<Value>True</Value>
</CustomField>
</CustomFields>
that goes into a bigger result:
<Trip>
<TripNumber>8510</TripNumber>
<EffectiveDOWs>
<EXCPT>EXCPT</EXCPT>
<DayOfWeek>MO</DayOfWeek>
<DayOfWeek>TH</DayOfWeek>
<DayOfWeek>FR</DayOfWeek>
</EffectiveDOWs>
<ReportTime>
<Hours>21</Hours>
<Minutes>40</Minutes>
</ReportTime>
<TripCode>N</TripCode>
<EffectiveDateStart>
<Month>APR</Month>
<Day>02</Day>
</EffectiveDateStart>
<EffectiveDateEnd>
<Month>APR</Month>
<Day>27</Day>
</EffectiveDateEnd>
<CustomFields>
<CustomField>
<Group>USAir</Group>
<Name>PairingCode</Name>
<DataType>String</DataType>
<Value>N</Value>
</CustomField>
</CustomFields>
<RequiredCrew>
<Captain>1</Captain>
<FO>1</FO>
</RequiredCrew>
.....snip....
</Trip>

I have reworked my custom ParseResults code, and it now works as expected. I wish I had thought of doing it this way the first time, as it was much easier to figure out. :) I do tend to reinvent the wheel... tripHeaderCustomFields is attached as a ParseAction, and the new ParseResults are added to the parent ParseResults.
This solves the mysterious problem I had with accessing the customField items by name. I am sure it was caused by my not really understanding how the parse results are created behind the scenes, so this works out much better for me.
def tripHeaderCustomFields( self, tokens ):
parseSegment = tokens
if "TripCode" in parseSegment:
customField = self.addCustomField( "USAir", "PairingCode", "String", parseSegment["TripCode"], parseSegment )
if "Charter" in parseSegment[self.NAME.EFFECTIVEDOWS]:
customField = self.addCustomField( "USAir", "Charter", "Boolean", "True", parseSegment )
def buildCustomFieldString( self, group, name, datatype, value ):
#TODO: replace any stray "|" that might be in input strings
text = group + "|" + name + "|" + datatype + "|" + value
return text
def addCustomField( self, group, name, datatype, value, token ):
"""
custom fields:
Group: ie, specific airline or category - "USAir, "general"
Name: name of field, ie "linecheck", "Medical", "Deadhead", "IV Pay"
DataType: string, int, date, time
Value: value of field, ie. "checked by joe shmo, #2345", or "1st class medical - bryman"
<CustomFields>
<CustomField>
<Group>USAir</Group>
<Name>EquipmentChange</Name>
<DataType>Boolean</DataType>
<Value>True</Value>
</CustomField>
<CustomField>
<Group>USAir</Group>
<Name>EquipmentChange</Name>
<DataType>Boolean</DataType>
<Value>True</Value>
</CustomField>
</CustomFields>
"""
pGroup = Word( alphanums )( self.NAME.CFGROUP )
pName = Word( alphanums )( self.NAME.CFNAME )
pDatatype = Word( alphanums )( self.NAME.CFDATATYPE )
pValue = Word( alphanums )( self.NAME.CFVAULE )
delim = Suppress( "|" )
customField = Group( pGroup + delim + pName + delim + pDatatype + delim + pValue )( self.NAME.CUSTOMFIELD )
text = self.buildCustomFieldString( group, name, datatype, value )
if self.NAME.CUSTOMFIELDS in token:
token[self.NAME.CUSTOMFIELDS] += customField.parseString( text )
else :
token += Group( customField )( self.NAME.CUSTOMFIELDS ).parseString( text )

Related

coercing a paramter into an integer in Body Mapping Template, AWS API Gateway

I've been using a bit of arithmetic in the Body Mapping Template in Integration Request:
#set($now = $context.requestTimeEpoch/1000)
#set($soon = $now + 600)
{
"TableName": "events",
.... [ here get events between $now and $soon]
}
Recently I came to need to pass an offset through a parameter:
#set($now = $context.requestTimeEpoch/1000)
#set($soon = $now + $input.params('offset'))
{
"TableName": "events",
.... [ here get events between $now and $soon] ....
}
It turns out that if $now is 1518939082, with query paramter ?offset=600 $soon will be 1518939082600 - a concatenation. I have tried various ways to force the parameter being recognised as an integer, including:
#set($offset = $Integer.parseInt($input.params('offset')))
#set($offset = 0 + $input.params('offset'))
#set($offset = 1 * $input.params('offset'))
None of them works. I inserted #set($offset = 0) before each test so I can tell "nothing happens" from "a nothingness is returned".
In the first case, $offset prints an empty string, not 0. (This happens to $Integer.parseInt("1") too.)
In the second case, $offset prints a concatenation of "0" and the string value of "offset".
In the third case, $offset prints a 0, as if the entire line doesn't exist.
None of them successfully transformed the parameter to an integer.
Is there a way to use that parameter as an integer?

X++ Compute columns

I try recreate SQL view in AOT and I need add compute column which reference view (FISCALCALENDARFLATTENEDVIEW). In the class I cannot retrieved fields for this view. Problem is with "sGREGORIANDATE = " line where I specify a fieldStr(), cannot do it for View. I put SQL statement just to help understand what I try achieve.
CREATE VIEW [dbo].[vw_DimFiscalPeriod] AS
SELECT
T1.RECID AS LEDGERRECID,
T1.PARTITION AS PARTITION,
T1.RECID AS RECID,
T2.CALENDARID AS CALENDARID,
T2.CALENDARRECID AS CALENDARRECID,
T2.GREGORIANDATE AS GREGORIANDATE,
T2.MONTH AS MONTH,
T2.PERIODNAME AS PERIODNAME,
T2.PERIODOFFSET AS PERIODOFFSET,
T2.PERIODRECID AS PERIODRECID,
T2.QUARTER AS QUARTER,
T2.QUARTEROFFSET AS QUARTEROFFSET,
T2.YEARNAME AS YEARNAME,
T2.YEAROFFSET AS YEAROFFSET,
T2.PARTITION AS PARTITION#2,
(CAST ((((T1.RECID) * (100000)) + (DateDiff(d, {ts '1900-01-01 00:00:00.000'}, T2.GREGORIANDATE))) AS NVARCHAR(50))) AS LEDGERGREGORIANDATEID
FROM AXDB.dbo.LEDGER T1
INNER JOIN
AXDB.dbo.FISCALCALENDARFLATTENEDVIEW T2
ON T1.FISCALCALENDAR = T2.CALENDARRECID
AND T1.PARTITION = T2.PARTITION
I created class but for my calculation need fields from both objects and column list for a view is not supported. sRECID is ok but sGREGORIANDATE definition is not.
public class vw_DimFiscalPeriod extends common
{
private static server str GregoriandDateID()
{
str sReturn,
str sRECID,
str sGREGORIANDATE;
DictView dictView;
dictView = new DictView(tableNum(vw_DimFiscalPeriod));
sRECID = dictView.computedColumnString(tableStr(Ledger), fieldStr(RecId),FieldNameGenerationMode::FieldList, true);
sGREGORIANDATE = dictView.computedColumnString(viewstr(FiscalCalendarFlattenedView), fieldStr(GregorinaDate), FieldNameGenerationMode::FieldList, true);
sReturn = "("+RECID+" * 100000) + DateDiff(d, {ts '1900-01-01 00:00:00.000'}, " + sGREGORIANDATE + " )";
return sReturn;
}
}
I think that should work and line for sGREGORIANDATE should looks like that:
sGREGORIANDATE = SysComputedColumn::returnField(tableStr(FiscalPeriodDateView), tableStr(FiscalCalendarFlattenedView), fieldStr(FiscalCalendarFlattenedView, GregorianDate)));

How to display all the properties of the selected reviewers?

Consider the business process "Review and Approve (one or more reviewers) - Assign a review task to multiple reviewers".
When I choose reviewers I see only their properties cm:userName. How to display all the properties of the type cm:person?
For example:
cm:userName
cm:firstName
cm:middleName
cm:email
cm:organizationId
cm:jobtitle
cm:googleusername
And so on...
Instead of this container (part of the association.ftl):
...
<div id="${controlId}-currentValueDisplay" class="current-values"></div>
...
I would like to use table. IMHO for that I should override some parts of the
Alfresco.ObjectFinder, such as:
if (this.options.displayMode == "items")
{
Dom.get(this.id + "-currentValueDisplay").innerHTML = displayValue;
}
...etc. But how to display all the properties of the selected reviewers?
Let's say, this part:
displayValue +=
this.options.objectRenderer.renderItem(
item,
16,
"<div class='itemtype-" + $html(item.type) +
"' style='word-wrap: break-word;'>{icon} {name}</div>"
);
I assume that it's property is name
Ok, then where to find the mapping "property in object-finder : property in the person type"?
To display the companyname, email etc fields to you need to modify the following files in the repo side.
C:\Alfresco\tomcat\webapps\alfresco\WEB-INF\classes\alfresco\extension\templates\webscripts\org\alfresco\repository\forms\pickerresults.lib.js
C:\Alfresco\tomcat\webapps\alfresco\WEB-INF\classes\alfresco\extension\templates\webscripts\org\alfresco\repository\forms\pickerresults.lib.ftl
In the pickerresults.lib.js, I added the required extra properties in the CreatePersonResult(node) method.
function createPersonResult(node)
{
var personObject =
{
typeShort: node.typeShort,
isContainer: false,
properties: {},
displayPath: node.displayPath,
nodeRef: "" + node.nodeRef
}
// define properties for person
personObject.properties.userName = node.properties.userName;
personObject.properties.name = (node.properties.firstName ? node.properties.firstName + " " : "") +
(node.properties.lastName ? node.properties.lastName : "") +
" (" + node.properties.userName + ")";
personObject.properties.jobtitle = (node.properties.jobtitle ? node.properties.jobtitle : "");
//Add the extra properties here
personObject.properties.organization =(node.properties.organization ? node.properties.organization : "");
personObject.properties.googleusername =(node.properties.googleusername ? node.properties.googleusername : "");
return personObject;
}
In the pickerresults.lib.ftl, I added the extra properties to the result set, below to the "selectable" : ${row.selectable?string}</#if>
"nodeRef": "${row.item.nodeRef}"<#if row.selectable?exists>,
"selectable" : ${row.selectable?string}</#if>,
"company": "${row.item.properties.organization!""}",
"googleusername": "${row.item.properties.googleusername!""}",
"jobtitle": "${row.item.properties.jobtitle!""}"
Hope this helps you now.
Object-finder.js is used for different kind of objects like person, tags etc.
item.type is cm:person, but it doesn't have all the person of person object here. Refer the below image.

How can I create xquery using xml response

The below mentioned is actual xquery.
declare variable $rankCode_DL3UJ :=("CPT","FO","FM");
declare variable $rankCode_DL4UJ :=("CRFO","CL");
declare variable $type :=("DOM","ISH");
declare function local:getboardingPriorityCode($flightType as element(), $rankCode as element()) as xs:string
{
if(data($flightType) = $type) then
'DHD'
else
if(data($rankCode) = $rankCode_DL3UJ) then
'DL3UJ'
else if(data($rankCode) = $rankCode_DL4UJ) then
'DL4UJ'
else
'DL5UJ'
};
declare function local:getPreferredClass($flightType as element(flightCategory:Code)) as xs:string
{
if(data($flightType) = $type) then
'Y'
else
'J'
};
declare function xf:createDeadheadReservationRQ-to- PassengerDetailsRQ($createDeadheadReservationRQ1 as element(ns1:CreateDeadheadReservationRQ),$CreateDeadHeadReservationAdapterSelect RS as element(ns3:RefTickettypeCollection),
$airBookRS as element(flightCategory:GetFlightCategoryRS))
as element(ns2:PassengerDetailsRQ) {
<ns2:PassengerDetailsRQ Version = "2.0.0">
<ns2:SpecialReqDetails>
<ns2:AddRemarkRQ>
<ns2:RemarkInfo>
<ns2:Remark Type = "General">
<ns2:Text>{ data($createDeadheadReservationRQ1/ns1:SpecialRequestDetails/ns1:Remark) }</ns2:Text>
</ns2:Remark>
</ns2:RemarkInfo>
</ns2:AddRemarkRQ>
<ns2:SpecialServiceRQ>
<ns2:SpecialServiceInfo>
<ns2:SecureFlight SegmentNumber = "A" SSR_Code = "DOCS">
<ns2:PersonName DateOfBirth = "{ data($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:DateOfBirth) }"
Gender = "{ data($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:Gender) }"
NameNumber = "1.1">
<ns2:GivenName>{ data($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:PassengerName/ns1:GivenName) }</ns2:GivenName>
<ns2:Surname>{ data($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:PassengerName/ns1:Surname) }</ns2:Surname>
</ns2:PersonName>
<ns2:VendorPrefs>
<ns2:Airline Hosted = "true"/>
</ns2:VendorPrefs>
</ns2:SecureFlight>
<ns2:Service SSR_Code = "{ data($createDeadheadReservationRQ1/ns1:SpecialRequestDetails/ns1:RankBasedSSR) }">
<ns2:VendorPrefs>
<ns2:Airline Hosted = "true"/>
</ns2:VendorPrefs>
</ns2:Service>
</ns2:SpecialServiceInfo>
</ns2:SpecialServiceRQ>
</ns2:SpecialReqDetails>
<ns2:TravelItineraryAddInfoRQ>
<ns2:AgencyInfo>
<ns2:Ticketing TicketType = "{fn:concat('7PS-', local:getboardingPriorityCode($airBookRS/flightCategory:ServiceType/flightCategory:Code, $createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:RankCode), local:getPreferredClass($airBookRS/flightCategory:ServiceType/flightCategory:Code))}"/>
</ns2:AgencyInfo>
<ns2:CustomerInfo>
<ns2:PersonName>
<ns2:GivenName>{
xs:string(fn:concat($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:PassengerName/ns1:GivenName,' ',
$createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:PassengerName/ns1:Title))
}</ns2:GivenName>
<ns2:Surname>{ xs:string($createDeadheadReservationRQ1/ns1:PassengerInfo/ns1:PassengerName/ns1:Surname) }</ns2:Surname>
</ns2:PersonName>
</ns2:CustomerInfo>
</ns2:TravelItineraryAddInfoRQ>
</ns2:PassengerDetailsRQ>
};
declare variable $createDeadheadReservationRQ1 as element(ns1:CreateDeadheadReservationRQ) external;
declare variable $airBookRS as element(flightCategory:GetFlightCategoryRS) external;
declare variable $CreateDeadHeadReservationAdapterSelectRS as element(ns3:RefTickettypeCollection) external;
xf:createDeadheadReservationRQ-to- PassengerDetailsRQ($createDeadheadReservationRQ1, $airBookRS, $CreateDeadHeadReservationAdapterSelectRS)
So We have to do the below changes.
for that we have created the below mentioned table in DB, using JCA adapter we have received the response and created the xml response creating business service of JCA adapter in OSB.
condition: In query we need to fetch OTHER always so query will be as follows and if we get 2 results then pick one that is not “other” like pick CPT if it returned in result of select query and if we get 1 result then it will be always “Other”.
Select *from <Table> where Code_name in (‘OTHER’, 'value received in request');
xml request and response of business service JCA adapter:
Request:
<cre:CreateDeadHeadReservationAdapterSelect_CODE_NAMEInputParameters>
<cre:CODE_NAME>CPT</cre:CODE_NAME>
</cre:CreateDeadHeadReservationAdapterSelect_CODE_NAMEInputParameters>
Response:
<cre:RefTickettypeCollection xmlns:xsi="http://www.w3.org/2001/XMLSchema- instance" xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/" xmlns:cre="http://xmlns.oracle.com/pcbpel/adapter/db/top/CreateDeadHeadReservati onAdapter">
<cre:RefTickettype>
<cre:codeName>CPT</cre:codeName>
<cre:codeType>Rank</cre:codeType>
<cre:priorityCode>DL3UJ</cre:priorityCode>
<cre:preferredClass>J</cre:preferredClass>
</cre:RefTickettype>
<cre:RefTickettype>
<cre:codeName>OTHER</cre:codeName>
<cre:codeType>Other</cre:codeType>
<cre:priorityCode>DL7UJ</cre:priorityCode>
<cre:preferredClass>J</cre:preferredClass>
</cre:RefTickettype></cre:RefTickettypeCollection>
So using the above response could you pls help me to create the xquery logic.
I'm not sure I understand all of your question. But, it sounds like when two RefTickettype elements are returned, you want to select the one where codeName is not OTHER?
The following query checks to see how many RefTickettype elements are in the result. If there is only one, it is returned. If there is more than one, it will return all where codeName is not OTHER.
declare namespace cre="http://xmlns.oracle.com/pcbpel/adapter/db/top/CreateDeadHeadReservationAdapter";
let $response :=
<cre:RefTickettypeCollection>
<cre:RefTickettype>
<cre:codeName>CPT</cre:codeName>
<cre:codeType>Rank</cre:codeType>
<cre:priorityCode>DL3UJ</cre:priorityCode>
<cre:preferredClass>J</cre:preferredClass>
</cre:RefTickettype>
<cre:RefTickettype>
<cre:codeName>OTHER</cre:codeName>
<cre:codeType>Other</cre:codeType>
<cre:priorityCode>DL7UJ</cre:priorityCode>
<cre:preferredClass>J</cre:preferredClass>
</cre:RefTickettype>
</cre:RefTickettypeCollection>
return
if (count($response/cre:RefTickettype) eq 1) then
$response/cre:RefTickettype
else
$response/cre:RefTickettype[cre:codeName ne "OTHER"]

Printing the results from a select query with Genie

I have created the database in SQL lite and improved the little program to handle it (list, add, remove records). At this point I am trying to list the contents from the database using the prepared statement step() function. However, I can't iterate over the rows and columns on the database.
I suspect that the reason for that is that I am not handling the statement appropriately in this line:
stmt:Sqlite.Statement = null
If that is the case, how to pass the statement from the main (init) function to the children function?
This is the entire code so far:
// Trying to do a cookbook program
// raw_imput for Genie included, compile with valac --pkg sqlite3 cookbook.gs
[indent=4]
uses Sqlite
def raw_input (query:string = ""):string
stdout.printf ("%s", query)
return stdin.read_line ()
init
db : Sqlite.Database? = null
if (Sqlite.Database.open ("cookbook.db3", out db) != Sqlite.OK)
stderr.printf ("Error: %d: %s \n", db.errcode (), db.errmsg ())
Process.exit (-1)
loop:bool = true
while loop = true
print "==================================================="
print " RECIPE DATABASE "
print " 1 - Show All Recipes"
print " 2 - Search for a recipe"
print " 3 - Show a Recipe"
print " 4 - Delete a recipe"
print " 5 - Add a recipe"
print " 6 - Print a recipe"
print " 0 - Exit"
print "==================================================="
response:string = raw_input("Enter a selection -> ")
if response == "1" // Show All Recipes
PrintAllRecipes()
else if response is "2" // Search for a recipe
pass
else if response is "3" //Show a Recipe
pass
else if response is "4"//Delete a recipe
pass
else if response is "5" //Add a recipe
pass
else if response is "6" //Print a recipe
pass
else if response is "0" //Exit
print "Goodbye"
Process.exit (-1)
else
print "Unrecognized command. Try again."
def PrintAllRecipes ()
print "%-5s%-30s%-20s%-30s", "Item", "Name", "Serves", "Source"
print "--------------------------------------------------------------------------------------"
stmt:Sqlite.Statement = null
param_position:int = stmt.bind_parameter_index ("$UID")
//assert (param_position > 0)
stmt.bind_int (param_position, 1)
cols:int = stmt.column_count ()
while stmt.step () == Sqlite.ROW
for i:int = 0 to cols
i++
col_name:string = stmt.column_name (i)
val:string = stmt.column_text (i)
type_id:int = stmt.column_type (i)
stdout.printf ("column: %s\n", col_name)
stdout.printf ("value: %s\n", val)
stdout.printf ("type: %d\n", type_id)
/* while stmt.step () == Sqlite.ROW
col_item:string = stmt.column_name (1)
col_name:string = stmt.column_name (2)
col_serves:string = stmt.column_name (3)
col_source:string = stmt.column_name (4)
print "%-5s%-30s%-20s%-30s", col_item, col_name, col_serves, col_source */
Extra questions are:
Does the definitions of functions should come before or after init? I have noticed that they wouldn't be called if I left all of them after init. But by leaving raw_input in the beginning the error disappeared.
I was trying to define PrintAllRecipes() within a class, for didactic reasons. But I ended up making it "invisible" to the main routine.
Many thanks,
Yes, you need to assign a prepared statement, not null, to stmt. For example:
// Trying to do a cookbook program
// raw_input for Genie included, compile with
// valac --pkg sqlite3 --pkg gee-0.8 cookbook.gs
[indent=4]
uses Sqlite
init
db:Database
if (Database.open ("cookbook.db3", out db) != OK)
stderr.printf ("Error: %d: %s \n", db.errcode (), db.errmsg ())
Process.exit (-1)
while true
response:string = UserInterface.get_input_from_menu()
if response is "1" // Show All Recipes
PrintAllRecipes( db )
else if response is "2" // Search for a recipe
pass
else if response is "3" //Show a Recipe
pass
else if response is "4"//Delete a recipe
pass
else if response is "5" //Add a recipe
pass
else if response is "6" //Print a recipe
pass
else if response is "0" //Exit
print "Goodbye"
break
else
print "Unrecognized command. Try again."
namespace UserInterface
def get_input_from_menu():string
show_menu()
return raw_input("Enter a selection -> ")
def raw_input (query:string = ""):string
stdout.printf ("%s", query)
return stdin.read_line ()
def show_menu()
print """===================================================
RECIPE DATABASE
1 - Show All Recipes
2 - Search for a recipe
3 - Show a Recipe
4 - Delete a recipe
5 - Add a recipe
6 - Print a recipe
0 - Exit
==================================================="""
namespace PreparedStatements
def select_all( db:Database ):Statement
statement:Statement
db.prepare_v2( """
select name, servings as serves, source from Recipes
""", -1, out statement )
return statement
def PrintAllRecipes ( db:Database )
print "%-5s%-30s%-20s%-30s", "Item", "Name", "Serves", "Source"
print "--------------------------------------------------------------------------------------"
stmt:Statement = PreparedStatements.select_all( db )
cols:int = stmt.column_count ()
var row = new dict of string, string
item:int = 1
while stmt.step() == ROW
for i:int = 0 to (cols - 1)
row[ stmt.column_name( i ) ] = stmt.column_text( i )
stdout.printf( "%-5s", item.to_string( "%03i" ))
stdout.printf( "%-30s", row[ "name" ])
stdout.printf( "%-20s", row[ "serves" ])
stdout.printf( "%-30s\n", row[ "source" ])
item++
A few pointers
Generally you want to avoid assigning null. null is no value. For example a boolean can either be true or false and nothing else, but a variable that can have no value makes things more complicated.
a:bool? = null
if a == null
print "I'm a boolean variable, but I am neither true nor false???"
If you are looking to declare a variable in Genie before assigning a value, for example when calling a function with an out parameter, don't assign anything. I have changed db:Database to show this
Process.exit( -1 ) should probably be used sparingly and really only for error conditions that you want to signal to a calling command line script. I don't think a user selected exit from the program is such an error condition, so I have changed Process.exit( -1 ) to break for that
The definition of functions doesn't matter whether it is before or after init, I prefer to put them after so the first function that is called, i.e. init, is at the top and easy to read
A class is a data type and yes, it can have functions, but usually you need some data defined in the class and the function is written to act on that data. A function in a class is often called a 'method' and in the past with object oriented programming classes were defined to group methods together. These methods had no data to act on and are defined as 'static' methods. The modern practise is to mainly use static methods for creating more complex object constructors, look up 'factory' methods and creational design patterns. Instead to group functions, and other syntax, we use namespaces. I have used a couple of namespaces in the example. Usually a namespace is given its own file or files. If you are thinking of splitting your Genie project into more source files then take a look at https://wiki.gnome.org/Projects/Genie#A_Simple_Build_Script
A primary key should be internal to the database and would not be presented to a user, only a database administrator would be interested in such things. So I have changed 'item' in the output to be a count of the number of entries displayed
Genie and Vala bind the SQLite C interface. If you need more details on a particular function take a look at C-language Interface Specification for SQLite

Resources