In Lianja 9.5 I have completed the example_timeclock App that is provided with the Lianja App Builder.
This is now a fully functional "Factory Time Clock" App for employees to clock in, clock out and manage the clock and employees.
It is created as a "standalone" Lianja App and can be installed on any machine without Lianja Runtime being present.
You can study this small App to see how to build standalone desktop Apps in Lianja.
You login as "admin" to be able to manage the clock and the employees.
You login as "user" to leave the timeclock app open for employees to login.
The "setup.prg" in the example_timeclock App can be customized. If required you can set datadir to a network drive on a LAN to share data across multiple time clocks. e.g
Code:
set datadir to f:\data\
The "parameters" property in example_timeclock.prg can also be used to customize the "login" form.
I intend at a later date to add face recognition into this App using python opencv. LianjaScript and Python are closely integrated so you can use all the Python modules for AI and image manipulation.
Details can be found at:
https://medium.com/@gowtham180502/ho...n-61d8d953a942
Feel free to enhance this App for your specific needs.
For anyone interested this is the source code that you can read and study:
File: setup.prg
Code:
// this is called when the app runs to setup databases, tables etc.
if not Lianja.standalone
return
endif
set debugout on
debugout "running setup.prg"
debugout "filepath: "+Lianja.standalonefilepath
if not databaseExists("factory")
debugout "database factory does not exist"
create database factory
try
open database factory
create table employees (badgeno char(30), firstname char(30), lastname char(30), photo blob)
insert into employees (badgeno, firstname, lastname) values ("A1234", "John", "Doe")
create table timeclock (badgeno char(30), clockin datetime, clockout datetime, clockinphoto blob, clockoutphoto blob)
catch
debugout "An error occurred"
endtry
endif
close database
debugout "setup completed successfully"
File: example_timeclock.prg
Code:
//
// example_timeclock App
//
// When this app runs as a standalone app the setup.prg script is run to setup datadir, databases, tables etc
//
//
// database "factory" and its tables are created in setup.prg of they don't exist
//
// optional to share data between windows and linux: set samba on
//
open database factory
local oform = createObject("Form")
// Standalone Apps can optionally require login
oform.requireslogin = 1
// You can customize the login page using form "parameters"
oform.parameters = "login.caption=Factory Time Clock;login._background=darkred;login.backgroundimage=img/wallpapers/background.png;login.version=Time Clock Version 1.0;login.copyright=Copyright (c) (2024) LianjaDev. All Rights Reserved Worldwide."
oform.resize(700, 600)
oform.closable = 1
oform.caption = "Factory Time clock"
oform.minwidth = 700
oform.minheight = 600
// clicking the window "close" button will exit the standalone App
oform.exitonclose = 1
// move the window to the top of the screen beneath the camera
oform.move(screenWidth() / 2 - 700 / 2, 20)
oform.addObject("ocont", "container")
ocont.margin = 10
ocont.autosize = 1
ocont.layout = "V"
ocont.backcolor = "black"
ocont.addObject("obadgenolab", "label")
obadgenolab.fixedheight = 24
obadgenolab.autowidth = 1
obadgenolab.alignment = "center"
obadgenolab.caption = "Enter Badge Number"
obadgenolab.backcolor = "darkgray"
obadgenolab.forecolor = "white"
obadgenolab.fontbold = 1
obadgenolab.fontsize = 10
ocont.addspacing(10)
ocont.addObject("obadgeno", "textbox")
obadgeno.fixedheight = 24
obadgeno.autowidth = 1
obadgeno.inputmask = "@!"
obadgeno.placeholder = "Enter your badge number, then CLOCK IN or CLOCK OUT"
ocont.addspacing(10)
ocont.addObject("obadgenolab2", "label")
obadgenolab2.fixedheight = 24
obadgenolab2.autowidth = 1
obadgenolab2.alignment = "center"
obadgenolab2.caption = "Take a Photo"
obadgenolab2.backcolor = "darkgray"
obadgenolab2.forecolor = "white"
obadgenolab2.fontbold = 1
obadgenolab2.fontsize = 10
ocont.addspacing(10)
ocont.addObject("ocamera", "camera")
ocamera.autowidth = 1
ocamera.autoheight = 1
ocamera.backcolor = "black"
ocont.addspacing(10)
ocont.addObject("obadgenolab3", "label")
obadgenolab3.fixedheight = 24
obadgenolab3.autowidth = 1
obadgenolab3.alignment = "center"
obadgenolab3.caption = "Clock in / Clock out"
obadgenolab3.backcolor = "darkgray"
obadgenolab3.forecolor = "white"
obadgenolab3.fontbold = 1
obadgenolab3.fontsize = 10
ocont.addObject("ocont2", "container")
ocont2.layout = "H"
ocont2.fixedheight = 60
ocont2.backcolor = "black"
ocont2.spacing = 10
ocont2.addObject("obtn1", "commandbutton")
obtn1.resize(60, 50)
obtn1.flat = 1
obtn1.fixedheight = 50
obtn1.caption = "CLOCK" + chr(10) + "IN"
obtn1.stylesheet = "btn"
ocont2.addObject("obtn2", "commandbutton")
obtn2.flat = 1
obtn2.resize(60, 50)
obtn2.fixedheight = 50
obtn2.stylesheet = "btn"
obtn2.caption = "CLOCK" + chr(10) + "OUT"
ocont2.addObject("obtn3", "commandbutton")
obtn3.flat = 1
obtn3.resize(60, 50)
obtn3.fixedheight = 50
obtn3.stylesheet = "btn"
obtn3.caption = "MANAGE" + chr(10) + "CLOCK"
ocont2.addObject("obtn4", "commandbutton")
obtn4.flat = 1
obtn4.resize(60, 50)
obtn4.fixedheight = 50
obtn4.stylesheet = "btn"
obtn4.caption = "MANAGE" + chr(10) + "EMPLOYEES"
ocont2.addObject("ocntin", "label")
ocntin.resize(60, 50)
ocntin.fixedheight = 50
ocntin.alignment = "center"
ocntin.fontsize = 16
ocntin.backcolor = "black"
ocntin.forecolor = "white"
ocntin.caption = "Clocked In<br>0"
ocont2.addObject("ocntout", "label")
ocntout.resize(60, 50)
ocntout.fixedheight = 50
ocntout.alignment = "center"
ocntout.fontsize = 16
ocntout.backcolor = "black"
ocntout.forecolor = "white"
ocntout.caption = "Clocked Out<br>0"
proc updateCounts()
m_cnt = sqlEval("select cnt(*) from timeclock where empty(clockout)")
ocntin.caption = "Clocked In<br>" + etos(m_cnt)
m_cnt = sqlEval("select cnt(*) from timeclock where not empty(clockout)")
ocntout.caption = "Clocked Out<br>" + etos(m_cnt)
endproc
// event handlers
proc clockin()
local cBadgeno = alltrim(obadgeno.text)
if len(alltrim(cBadgeno)) = 0
thisform.showWarningMessage("Please must specify your badge number")
return
endif
local m_cnt = sqlEval("select cnt(*) from employees where badgeno='&cBadgeno'")
if etos(m_cnt) = "0"
thisform.showWarningMessage("Unrecognized badge number!")
return
endif
local m_clockout = sqlEval("select clockout from timeclock where badgeno='&cBadgeno' and empty(clockout)")
if len(alltrim(m_clockout)) > 0
thisform.showWarningMessage("You must clock out before you can clock in again!")
return
endif
obadgeno.text = ""
local cPhoto = ocamera.takePhoto()
insert into timeclock (badgeno, clockin, clockinphoto) values (cBadgeno, datetime(), cPhoto)
if _tally = 1
thisform.showSuccessMessage("You clocked in on " + dtoc(date()) + " at " + ampm())
else
thisform.showErrorMessage("Failed to clock in")
endif
updateCounts()
endproc
proc clockout()
local cBadgeno = alltrim(obadgeno.text)
if len(alltrim(cBadgeno)) = 0
thisform.showWarningMessage("Please must specify your badge number")
return
endif
local m_cnt = sqlEval("select cnt(*) from employees where badgeno='&cBadgeno'")
if etos(m_cnt) = "0"
thisform.showWarningMessage("Unrecognized badge number!")
return
endif
obadgeno.text = ""
local m_cnt = sqlEval("select cnt(*) from timeclock where badgeno='&cBadgeno' and empty(clockout)")
if etos(m_cnt) = "0"
thisform.showWarningMessage("You must clock in before you can clock out!")
return
endif
local cPhoto = ocamera.takePhoto()
update timeclock set clockout = datetime(), clockoutphoto = "&cPhoto" where badgeno='&cBadgeno' and empty(clockout)
if _tally = 1
thisform.showSuccessMessage("You clocked out on " + dtoc(date()) + " at " + ampm())
else
thisform.showErrorMessage("Failed to clock out")
endif
updateCounts()
endproc
proc admin()
use timeclock
browse splitbar ;
fields badgeno:h="Badge Number", clockin:h="Clock In Time", clockout:h="Clock Out Time" ;
properties "splitimagelist=clockinphoto:Clock In Photo,clockoutphoto:Clock Out Photo" ;
title "Manage Clock"
use
updateCounts()
endproc
proc employees()
use employees
browse splitbar ;
fields badgeno:h="Badge Number", firstname:h="First Name", lastname:h="Last Name" ;
properties "splitimagelist=photo:Photo" ;
title "Manage Employees"
use
updateCounts()
endproc
proc afterlogin()
if username() != "admin"
obtn3.hide()
obtn4.hide()
ocntin.hide()
ocntout.hide()
endif
endproc
// Assign event handlers for the events on UI controls
oform.afterlogin = afterlogin
obtn1.click = clockin
obtn2.click = clockout
obtn3.click = admin
obtn4.click = employees
// modal form
oform.show(1)
// must put read events at the bottom so the window stays open until closed by user
read events
See also
https://www.lianja.com/community/sho...5879#post25879
Bookmarks