Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.project
124 changes: 124 additions & 0 deletions csvtable.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
#!/usr/bin/python3

# Convert PFS:First Choice database (.FOL) to CSV format
# David Schmidt 2026

try:
import sys
except:
print("Please install the sys module")
print("\tit should be part of the standard python3 distribution")
raise

try:
import csv
except:
print("Please install the csv module")
print("\tit should be part of the standard python3 distribution")
raise

try:
import argparse
except:
print("Please install the argparse module")
print("\tit should be part of the standard python3 distribution")
raise

try:
import signal
except:
print("Please install the signal module")
print("\tit should be part of the standard python3 distribution")
raise

import first
import common

def signal_handler(signal, frame):
"""Signal handler for clean exit on Ctrl-C"""
sys.exit(0)

def FOL_to_CSV(fol_handler, csv_file):
"""Convert FOL data to CSV format

Args:
fol_handler: FOL_handler instance with loaded data
csv_file: Open file object for CSV output
"""
# Get field names from form definition
field_names = [f['field'] for f in fol_handler.form['fields']]

# Create CSV writer
writer = csv.writer(csv_file)

# Write header row
writer.writerow(field_names)

# Write data rows
for record in fol_handler.data:
writer.writerow(record)

def CommandLine():
"""Setup argparser object to process the command line"""
cl = argparse.ArgumentParser(
description="Convert PFS:First Choice v3 database file (.FOL) to CSV format. 2021 by Paul H Alfille"
)

cl.add_argument(
"In",
help="Input database file (type .FOL)",
type=argparse.FileType('rb')
)

cl.add_argument(
"Out",
help="Output CSV file (default: stdout)",
nargs='?',
type=argparse.FileType('w', encoding='utf-8'),
default=sys.stdout
)

cl.add_argument(
"-v", "--verbose",
help="Verbose output",
action="count",
default=0
)

return cl.parse_args()

if __name__ == '__main__':
"""
First Choice FOL to CSV converter
"""
common.args = CommandLine()

# Set up keyboard interrupt handler
signal.signal(signal.SIGINT, signal_handler)

# Read in database (FOL file already open from command line)
try:
dbase_class = first.FOL_handler(common.args.In)
except common.User_Error as error:
print("Error parsing database file: {}".format(error), file=sys.stderr)
sys.exit(1)
except Exception as error:
print("Unexpected error: {}".format(error), file=sys.stderr)
sys.exit(1)

# Convert to CSV
try:
FOL_to_CSV(dbase_class, common.args.Out)
if common.args.verbose:
print(
"Converted {} records with {} fields".format(
len(dbase_class.data),
len(dbase_class.form['fields'])
),
file=sys.stderr
)
except Exception as error:
print("Error writing CSV: {}".format(error), file=sys.stderr)
sys.exit(1)

sys.exit(0)
5 changes: 3 additions & 2 deletions first.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,8 @@ def Parse( self, inputstring ):
if not self.FitOld(sl):
sl = self.wrap(ss)
# get rid of fake indent that takes place of field name
sl[0] = ' '+sl[0].lstrip()
if len(sl) > 0:
sl[0] = ' '+sl[0].lstrip()

if not self._final:
self.PadLines( sl )
Expand Down Expand Up @@ -1043,7 +1044,7 @@ def CommandLine():

# Read in databaase (FOL file already open from command line)
try:
dbase_class = FOL_handler( args.In, args.Out )
dbase_class = FOL_handler( common.args.In, common.args.Out )
except common.User_Error as error:
print("Error processing file: {}".format(error))
dbase_class = None
Expand Down
3 changes: 2 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,8 @@ While extracting data is nice, it would be nice to have a server-based multiuser
## Alternatives
* [FirstOut](https://file-convert.com/fout.htm) -- Comercial, extract only
* [First2html](http://first2html.sourceforge.net/) -- Open Source, extract only
* The module first.py can as standalone to parse and write a FOL file. It is used by web program for the FOL parsing.
* The module first.py can be used standalone to parse and write a FOL file. It is used by web program for the FOL parsing
* The module csvtable.py can be used standalone to parse a FOL file and write an equivalent .csv file.

## License

Expand Down