Глава 6:Пример разработки: агентство по найму

 Одна из многих интересных программ, пришедших мне на ум, когда я увидел пример Laser, поставляемый с FoxPro, была разработка для агентства по найму. Способность быстро перемещаться по базе данных очень заманчива. Использование memo-полей FoxPro для хранения текста резюме кажется совершенно естественным. Применение маленького трюка, заключающегося в создании индекса на индекс, позволяет выполнять сверхбыстрый поиск по ключевым словам.

Файлы и индексы


Большинство полагают, что для поиска всех подходящих резюме нужно провести поиск во всех memo-полях на предмет вхождения в текст нужной подстроки. Это может и неплохо, но после ввода в базу данных 5000 резюме такой метод работает довольно медленно. Попробуем что-нибудь побыстрее.

Я построил файл с данными о соискателях, со структурой, представленной в Табл.6-1.KEYWORDS- - текстовое поле, куда вы вводите слова, действительно важные для быстрого поиска. Это похоже на квадратики, которые нужно зачеркивать на бумажных формах, заполняемых во многих агентствах по найму. В Табл. 6-2 приведены структуры индексных файлов.

Технология поиска

Почему бы просто не определить несколько полей, представляемых на экране как независимые переключатели? Тому есть две причины. Во- первых, число таких переключателей, необходимых для представления даже самого общего списка требующихся специальностей в области, скажем, обработки данных, перевалит за сотню. Только перечисление наиболее часто встречающегося оборудования и программ займет несколько страниц. Кроме того выход того или иного пакета из обращения заставит вас каждый год звать программиста для перестройки экрана и базы данных. Это неплохо с точки зрения постоянной работы, но неразумно дорого. Давайте попробуем составить конкуренцию японцам и создадим что-нибудь быстрое и дешевое, а?



Вместо независимых переключателей я даю возможность охотнику за головами ввести все необходимые ключевые слова и добавить их в отдельный файл, структура которого показана в Табл. 6-3. Добавление выполняется по выбору опции меню также как при любом изменении содержимого поля PERSONEL.KEYWORDS. Если вы редактируете поле, программа стирает все ключевые слова и затем перестраивает их. Процесс происходит быстро и не мешает работе. Результаты поистине впечатляют. Для проверки я проделал поиск в файле из 1000 записей с целью найти 12 парней, специалистов по программе MAGIC WAND. Поиск занял примерно 1/10 сек. на 286/12, что не так уж плохо.

Программа

Программа начинается с инициализации нескольких глобальных переменных, включая цвета (Листинг 6-1(База данных агенства по найму с возможностью поиска по ключевому слову)). Для имитации монохромного монитора на VGA используйте команду TESTCOLOR=NOT ISCOLOR().

Я предпочитаю рисовать рамку вокруг рабочей области экрана и текст пояснений ярко-желтым цветом по темно-синему полю. Если вы используете цветовые схемы, весь текст на экране выводится одним и тем же цветом.

Код в Листинге 6-2 получен генерацией экрана. Я взял его в программу, добавил несколько дополнительных окон и скопировал сгенерированный код меню в процедуру (Листинг 6-3). Вы можете оставить код в отдельном файле и использовать команду DO PERSMENU.MPR.

Система элементов управления, определяется кодом Листинга 6-4 и позволяет использовать мышь для перемещения между записями. Кроме того я написал специальную программу поиска, использующую файл с ключевыми словами, что прекрасно подходит для такого рода работы.

Листинг 6-5 представляет код анализа ввода для элемента управления, определяющего переменную whatnow. Он позволяет пользователю перемещаться по базе данных с помощью мыши или клавиатуры без активизации меню. Обратите внимание на процедуру поиска.

Листинг 6-6 представляет программу построения нового файла KEYWORDS по данным поля KEYWORDS файла Personel.Dbf (поле длиной 240 знаков). Вначале программа убирает артикли, предлоги и отдельные цифры. После чего вызывается процедура Parser, возвращающая массив, содержащий остальные слова. Непустые элементы массива добавляются в файл Keywords.

Функция в Листинге 6-7 организует запрос ввода ключевого слова для поиска. После этого она выполняет поиск в файле ключевых слов, выводит подходящих кандидатов в виде меню и дает пользователю возможность выбора одного из них.

Для обеспечения целостности файла ключевых слов я предполагаю, что пользователь изменил список ключевых слов. Вначале я удаляю все старые ключевые слова для кандидата и добавляю новые (Листинг 6-8).

Процедура в Листинге 6-9 похожа на ту, что использовалась ранее для создания нового файла ключевых слов, только она повторно использует ранее удаленные записи. Так как процедура OLDNEW помечает записи как удаленные, приведенная процедура часто использует заново записи, удаленные за мгновение до этого. При использовании кэширования процесс занимает доли секунды, поскольку удаленные записи все еще находятся в кэше.

Меню инициализируется в процедуре, представленной в Листинге 6-10. Для основных функций опциям меню назначены "горячие клавиши". Меню может быть активизировано клавишей ALT и нажатием первой буквы слова в строке меню.

Маленькая функция в Листинге 6-11 дает возможность пользователю просмотреть файл с данными в табличном виде. Я не подключаю меню BROWSE содержащее опцию поиска, но вы можете это сделать.

В Листинге 6-12 приведена процедура добавления записи. Я использую переменные, и все поля GET соответствуют определенным переменным. Процедура не выполняет добавление записи пока не будет определена информация для ввода в базу данных.

Я не могу допустить добавление повторяющихся записей, так что все подозрительные данные выводятся для подтверждения ввода. Вы можете использовать функцию SOUNDEX() (Листинг 6-13) для проверки ошибок набора, но я предполагаю, что в таких учреждениях имена вводят правильно, так как это их товар.

Функция PRUNE (Листинг 6-14) удаляет все ссылки на ключевое слово из всех резюме. Для программиста ссылки на вышедшие из употребления языки или модели компьютеров могут привести к возрастной дискриминации, независимо от того является ли это законным.

Вначале я проверяю есть ли такое слово вообще: это позволяет перехватить неправильный ввод и дает возможность организовать более понятное сообщение. Кроме того, я предпочитаю получить подтверждение до того как совершу что-либо потенциально опасное как в этом случае. Комьютеры действительно повышают нашу возможность испортить себе жизнь. Процедура, приведенная в Листинге 6-15, использует файл KEYWORDS для поиска резюме, содержащих удаляемое ключевое слово. Таким образом, вы читаете только те, что нуждаются в очистке. Такой подход гораздо быстрее чем LOCATE FOR KEYWORD $ PERSONEL.KEYWORDS, который, готов поспорить, многие программисты на xBASE использовали бы в таком случае.

При поиске фамилии, города и основного рода занятий я использую поиск по неполному соответствию (soft seek), отличающемуся от поиска при SET NEAR ON, предлагаемого FoxPro тем, что при этом программа находит и выводит на экран список со всеми подходящими кандидатами (Листинг 6-16). Поиск одинаково хорошо работает и при ситуации типа "почти попал" и при поиске по неполному значению строки.

Процедура в Листинге 6-17 позволяет пользователям перенаправить вывод данных на экран, принтер или в файл. Я использую слово просмотр (preview) когда речь идет о выводе на экран. Если ни один из кандидатов не соответствует критериям поиска, сообщаем об этом пользователю и не печатаем отчет (Листинг 6-18).

В процедуре обработки ошибок (Листинг 6-19) я позволяю программисту нажать Esc для приостановления работы программы и пройти по шагам для поиска ошибки. Если пользователь не нажмет Esc, он не будет знать, что здесь скрыт люк для разработчика. Если нажмет, то вам позвонят с вопросом. Обратите внимание, что я восстанавливаю системное меню для облегчения отладки. Если вы этого не сделаете, то не сможете открыть окно отладки в интерактивном режиме. Такой подход годится для разработок, предназначенных для собственного использования, но не вполне пригоден для заказчика на стороне.

Функция центрирования строки (Листинг 6-20) делает то же самое, что и функция PADC() FoxPro, только она не очищает всю строку перед выводом сообщения.

В Листинге 6-21 приведена команда вывода текста резюме с возможностью редактирования. Сообщение "Нажмите F10 для сохранения" помещено как заголовок окна.

Функция в Листинге 6-22 удаляет выбранное ключевое слово из всех полей Keywords. Она находит слово и сливает строки слева и справа. Вы можете написать функцию с единственной командой REPLACE, снабженной предложением IIF(IIF(IIF())), но такой вариант слишком трудно читается.

Процедура в Листинге 6-23 пытается найти определенное значение ключа в единственном поиске SEEK. Если поиск неудачен, искомая строка обрезается с конца по одному символу до тех пор пока не найдено соответствие (при этом должно быть установлено SET EXACT OFF). После чего все записи, соответствующие оставшемуся частичному значению ключа, выводятся в виде меню.

Процедура назначения вывода (Листинг 6-24) добавляет гибкости организации вывода и легко подключается к проекту. Я не пытался изменить прекрасный код из примера, поставляемого с FoxPro 2.0 (это взято из Laser.pjx, - прим. пер.).

При анализе функции в Листинге 6-25 обратите внимание как можно улучшить работу диалогов, делая недоступными элементы управления, более не соответствующие ходу развития диалога. Подобный подход программирования добавляет законченность многим такого рода диалогам.

Вот и все. Рассмотренная программа может быть расширена поиском, учитывающем вес факторов при поиске по составным ключам и выводящим тех кандидатов, для которых вес факторов превысил некую установленную величину. Без особых усилий вы можете заставить программу находить любых кандидатов, соответствующих любым критериям. Однако, это полностью уничтожает участие человека.

Заключение

Это приложение написано на классическом FoxPro 2.0. Оно использует READ CYCLE и экранные элементы управления для контроля за перемещением по базе данных, использует выпадающие меню для других функций. Программа работает быстро и гладко. Вы можете добавить различные средства к уже существующему проекту. Я надеюсь, что приведенный пример дает определенное представление как писать хорошие, быстро работающие программы, совмещая использование возможности конструктора экранов и меню с вашими собственными идеями.

Листинг 6-25

FUNCTION toprtval && toprint VALID
#region 1
IF toprint = 1
SHOW GET preview disabled
SHOW GET tofile disabled
SHOW GET printfile disabled
ELSE
SHOW GETS enabled
ENDIF

FUNCTION destval && tofile VALID
#region 1
IF tofile = 1
printfile = PUTFILE('Print File:',ALLTRIM(printfile),'TXT')
IF EMPTY(printfile)
tofile = 0
SHOW GETS enabled
ELSE
SHOW GET toprint disabled
SHOW GET preview disabled
ENDIF
SHOW GET printfile
ELSE
printfile = PUTFILE('Print File:',ALLTRIM(printfile),'TXT')
IF EMPTY(printfile)
tofile = 0
SHOW GET tofile
ENDIF
SHOW GETS enabled
ENDIF



FUNCTION prevuval && Preview VALID
#region 1
IF preview = 1
SHOW GET toprint disabled
SHOW GET tofile disabled
SHOW GET printfile disabled
ELSE
SHOW GETS enabled
ENDIF



FUNCTION FilNmVal && printfile VALID
#region 1
IF EMPTY(printfile)
tofile = 0
SHOW GETS enabled
ELSE
SHOW GET toprint disabled
SHOW GET preview disabled
tofile = 1
SHOW GET tofile
ENDIF



FUNCTION DeacRead && Read Level Deactivate
#region 1
IF WOUTPUT("ReptWind")
?? CHR(7)
RETURN .f.
ENDIF
RETURN .t. && DeacRead



FUNCTION Alarm
SET BELL TO 330,6
?? CHR(7)
SET BELL TO 20,2
?? CHR(7)
SET BELL TO 330,4
?? CHR(7)
SET BELL TO 440,12
?? CHR(7)


Function PersHelp
=Alarm()
WAIT WINDOW [Not implemented] TIMEOUT